JDK1.8新特性:函数式接口
Posted 流楚丶格念
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDK1.8新特性:函数式接口相关的知识,希望对你有一定的参考价值。
文章目录
函数式接口
函数式接口概念
函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
函数式接口可以被隐式转换为 lambda 表达式。
Lambda 表达式和方法引用(实际上也可认为是Lambda表达式)上。
@FunctionalInterface
我们常用的Runnable接口就是个典型的函数式接口,我们可以看到它有且仅有一个抽象方法run:
@FunctionalInterface
public interface Runnable
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
并且可以看到一个注解@FunctionalInterface,这个注解的作用是强制你的接口只有一个抽象方法。
如果有多个话直接会报错,如图:
idea错误提示:
翻译下:
函数式接口使用方式
我们直接上代码,首先定义一个函数式接口
package com.yyl.lambda;
@FunctionalInterface
public interface SingleAbstraMethodInterface
public abstract void singleMethod();
我们定一个test类,封装一个方法,将SingleAbstraMethodInterface当做参数传入方法,并打印一句话
package com.yyl.lambda;
public class TestDemo
public void testMethod(SingleAbstraMethodInterface single)
System.out.println("即将执行函数式接口外部定义方法");
single.singleMethod();
public static void main(String[] args)
TestDemo test = new TestDemo();
test.testMethod(new SingleAbstraMethodInterface()
@Override
public void singleMethod()
System.out.println("执行函数式接口定义方法");
);
运行结果如下:
是不是和我们预期结果一样。这个过程是不是有的同学已经发现,怎么这么像jdk里面的一些接口的使用,比如常用的Runnable接口。我们来看看代码。
public static void main(String[] args)
new Thread(new Runnable()
@Override
public void run()
System.out.println("run方法执行了");
).start();
再看下Runnable接口源码,是不是一样😜😜😜
@FunctionalInterface
public interface Runnable
public abstract void run();
这时,有的同学会说,我用Runnable接口可不是这么用的,我的写法比你优雅,我是这么写的。
没错函数式接口,函数式编程,我们可以使用lambda表达式的写法,可以让代码更优雅,具体可以参照我的前一篇帖子。
public static void main(String[] args)
new Thread(()->
System.out.println("run方法执行了");
).start();
常用函数式接口
JDK提供了大量常用的函数式接口以丰富Lambda的典型使用场景,它们主要在 java.util.function 包中被提供。
其实,jdk中给我们提供了很多的函数式接口,我们平时都会用到,只不过大家没有注意到而已,这里我结合实际代码讲解几个常用的函数式接口。想想大家平时常常用到stream流的各种方法来处理list。我看去stream类中看一下它提供的方法。
可以看到有几个出镜率较高的函数式接口:
- Supplier
- Comsumer
- Predicate
- Function
比如我们使用filter,需要实现的就是Predicate这个函数式接口:
List<Man> list = new LinkedList<>();
// 匿名内部类写法
list.stream().filter(new Predicate<Man>()
@Override
public boolean test(Man man)
if (man.getName().equals("如梦"))
return true;
return false;
);
// lanmda写法
list.stream().filter((man)->
if (man.getName().equals("如梦"))
return true;
return false;
);
Supplier
Supplier 接口被称之为生产型接口,指定接口的泛型是什么类型,那么接口中的 get 方法就会产生什么类型的数据。
我们追踪一下源码:
@FunctionalInterface
public interface Supplier<T>
/**
* Gets a result.
* @return a result
*/
T get();
Supplier接口的get方法没有入参,返回一个泛型T对象。我们来看几个使用的实例。先写一个实验对象Man,我们定义一个方法,创建对象,我们使用lambda的方式来调用并创建对象。
完整示例如下:
package com.yyl.lambda;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.function.Supplier;
class Man
private String name;
private int age;
public Man()
public String getName()
return name;
public void setName(String name)
this.name = name;
public int getAge()
return age;
public void setAge(int age)
this.age = age;
public Man(String name, int age)
this.name = name;
this.age = age;
public static Man create(final Supplier<Man> supplier)
return supplier.get();
public class SupplierDemo
public static void main(String[] args)
Man man1 = Man.create(Man::new);
Man man2 = Man.create(() -> new Man("玉如梦", 22));
Man man = null;
//orElseGet
Man man3 = Optional.ofNullable(man).orElseGet(Man::new);
System.out.println(man1);
System.out.println(man2);
System.out.println(man);
System.out.println(man3);
运行结果如下:
Comsumer
java.util.function.Consumer 接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据, 其数据类型由泛型决定。
源码如下:
@FunctionalInterface
public interface Consumer<T>
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after)
Objects.requireNonNull(after);
return (T t) -> accept(t); after.accept(t); ;
可以看到accept方法接受一个对象,没有返回值。
那么我们来实战,首先使用Lambda表达式声明一个Supplier的实例,它是用来创建Man实例;再使用Lambda表达式声明一个Consumer的实例,它是用于打印出Man实例的toString信息;最后Consumer消费了Supplier生产的Man。我们常用的forEach方法入参也是Consumer。
代码如下:
Supplier<Man> supplier = ()-> new Man("lucy",33);
Consumer<Man> consumer = (Man g)->
System.out.println(g.toString());
;
consumer.accept(supplier.get());
Predicate
需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使用 java.util.function.Predicate 接口。
源码如下:
@FunctionalInterface
public interface Predicate<T>
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return @code true if the input argument matches the predicate,
* otherwise @code false
*/
boolean test(T t);
可以看到test方法接受一个对象,返回boolean类型,这个函数显然是用来判断真假。那么我们用这个接口来构造一个判断Man条件的示例,还有我们常用的stream流中,filter的入参也是Predicate,代码如下
代码如下:
Supplier<Man> supplier = ()-> new Man("lucy",33);
Predicate<Man> man36 = (Man g)-> Objects.equals(g.getAge(),36);
Predicate<Man> man33 = (Man g)-> Objects.equals(g.getAge(),33);
boolean test33 = man33.test(supplier.get());
boolean test36 = man36.test(supplier.get());
System.out.println(supplier.get().getName() +"是否为[36] :"+test36);
System.out.println(supplier.get().getName() +"是否为[33] :"+test33);
运行结果如下:
Function
java.util.function.Function<T,R> 接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件, 后者称为后置条件。
源码如下:
@FunctionalInterface
public interface Function<T, R>
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
这个看到apply接口接收一个泛型为T的入参,返回一个泛型为R的返回值,所以它的用途和Supplier还是略有区别。还是一样我们举个栗子用代码来说明:
Supplier<Man> supplier = ()-> new Man("lucy",33);
Function<Man,String> manMark = (Man man)-> man.getName()+"的年龄是"+man.getAge();
System.out.println(manMark.apply(supplier.get()));
运行结果:
常用函数式接口相关扩展接口
这里我列举一些函数式接口扩展接口,大家自己研究,我没咋研究
Supplier相关拓展接口
接口名称 | 方法名称 | 方法签名 |
---|---|---|
Supplier | get | () -> T |
BooleanSupplier | getAsBoolean | () -> boolean |
DoubleSupplier | getAsDouble | () -> double |
IntSupplier | getAsInt | () -> int |
LongSupplier | getAsLong | () -> long |
Comsumer相关拓展接口
接口名称 | 方法名称 | 方法签名 |
---|---|---|
Consumer | accept | (T) -> void |
DoubleConsumer | accept | (double) -> void |
IntConsumer | accept | (int) -> void |
LongConsumer | accept | (long) -> void |
ObjDoubleConsumer | accept | (T, double) -> vo |
ObjIntConsumer | accept | (T, int) -> void |
ObjLongConsumer | accept | (T, long) -> void |
Predicate相关拓展接口
接口名称 | 方法名称 | 方法签名 |
---|---|---|
Predicate | test | (T) -> boolean |
BiPredicate | test | (T, U) -> boolean |
DoublePredicate | test | (double) -> bool |
IntPredicate | test | (int) -> boolean |
LongPredicate | test | (long) -> boolean |
Function相关的接口
接口名称 | 方法名称 | 方法签名 |
---|---|---|
Function | apply | (T) -> R |
BiFunction | apply | (T, U) -> R |
DoubleFunction | apply | (double) -> R |
DoubleToIntFunction | applyAsInt | (double) -> int |
DoubleToLongFunction | applyAsLong | (double) -> long |
IntFunction | apply | (int) -> R |
IntToDoubleFunction | applyAsDouble | (int) -> double |
IntToLongFunction | applyAsLong | (int) -> long |
LongFunction | apply | (long) -> R |
LongToDoubleFunction | applyAsDouble | (long) -> double |
LongToIntFunction | applyAsInt | (long) -> int |
ToDoubleFunction | applyAsDouble | (T) -> double |
ToDoubleBiFunction | applyAsDouble | (T, U) -> double |
ToIntFunction | applyAsInt | (T) -> int |
ToIntBiFunction | applyAsInt | (T, U) -> int |
ToLongFunction | applyAsLong | (T) -> long |
ToLongBiFunction | applyAsLong | (T, U) -> long |
以上是关于JDK1.8新特性:函数式接口的主要内容,如果未能解决你的问题,请参考以下文章