函数式接口的定义
1、 在java8中,满足下面任意一个条件的接口都是函数式接口:
- 被@FunctionalInterface注释的接口,满足函数式接口的约束。
- 没有被@FunctionalInterface注释的接口,但是满足函数式接口的约束。
- @函数式的约束:
- 接口有且只能有个一个抽象方法,只有方法定义,没有方法体 。
- 在接口中覆写Object类中的public方法,不算是函数式接口的方法。
- 在接口中的default方法,不算是函数式接口的方法。
- 在接口中的static方法,不算是函数式接口的方法。
2、以下是自定义函数式接口
//接口一
@FunctionalInterface
public interface FunctionInterfaceTest {
String getInfo(String input);
@Override
String toString(); //Object中的方法
@Override
boolean equals(Object obj); //Object中的方法
}
//接口二
@FunctionalInterface
public interface FunctionInterfaceTest {
String getInfo(String input);
default void print() {
System.out.println("test");
}
static void identity() { System.out.println("identity");
}
}
//接口三
public interface FunctionInterfaceTest {
String getInfo(String input);
}3、自定义函数式接口实例调用
public class Main {
public static void main(String[] args) {
/**
* 1、lambda表达式
* 这种形式最为直观,lambda表达式,接收一个String类型的参数,返回一个String类型的结果。
* 完全符合函数式接口FunctionInterfaceTest的定义
*/
FunctionInterfaceTest1 functionInterfaceTest1 = item -> item+1;
/**
* 2、方法引用
* Main方法当中的getInstance和getMessage方法接收一个参数,返回一个结果。符合函数式接口
* FunctionInterfaceTest的定义。
* 函数式接口只是定义了个方法的约定(接收一个String类型的参数,返回一个String类型的结果),
* 而对于方法内部进行何种操作则并没有做任何的限制。在这点上,跟java以前的版本中的实现类与接口之间的
* 关系很类似。不同的是,函数式接口更偏重于计算过程,约束了一个计算过程的输入和输出。
* 这种约束计算过程的输入和输出的形式的好处可以看一下joinStr方法。
*/
FunctionInterfaceTest1 functionInterfaceTest2 = Main::getInstance;
FunctionInterfaceTest1 functionInterfaceTest3 = new Main()::getMessage;
String msg = functionInterfaceTest1.getInfo("123");
String msg1 = joinStr("你好",functionInterfaceTest2);
String msg2 = joinStr("你好",functionInterfaceTest3);
System.out.println(msg);
System.out.println(msg1);
System.out.println(msg2);
//还有更简单的写法,高度抽象化,具体处理由使用者自己决定
String msg3 = joinStr("你好",item ->item+"!世界");
String msg4 = joinStr("你好!",item ->"世界,"+ item+"!");
System.out.println(msg3);
System.out.println(msg4);
/**
* 3、构造方法引用
* 构造函数的结构:接收输入参数,然后返回一个对象。这种约束跟函数式接口的约束很像。
* 所以只要“输入参数类型”与“输出参数类型”跟FunctionInterfaceTest中的方法约束相同,
* 就可以创建出FunctionInterfaceTest接口的实例,如下,String的构造方法中有
* new String(str)的构造方法,所以可以得到实例。
* 这里存在一个类型推断的问题,JDK的编译器已经帮我们自动找到了只有一个参数,且是String类型的构造方法。
* 这就是我们直接String::new,没有指定使用哪一个构造方法,却可以创建实例的原因
*/
FunctionInterfaceTest1 functionInterfaceTest4 = String::new;
}
private static String getInstance(String item){
return item+"!世界";
}
private String getMessage(String massage){
return "世界,"+ massage+"!";
}
private static String joinStr(String str, FunctionInterfaceTest1 functionTest){
return functionTest.getInfo(str);
}
}4、常用的函数式接口
常用的函数式接口主要有四种类型,是通过其输入和输出的参数来进行区分的。定义了编码过程中主要的使用场景。
接口名 说明 Function<T,R> 接收一个T类型的参数,返回一个R类型的结果 Consumer<T> 接收一个T类型的参数,不返回值 Predicate<T> | 接收一个T类型的参数,返回一个boolean类型的结果 |
Supplier<T> 不接受参数,返回一个T类型的结果
5、Java8中对于接收两个参数的场景提供了相关的函数式接口
BiFunction<T, U, R> 接收T类型和U类型的两个参数,返回一个R类型的结果BiConsumer<T , U> 接收T类型和U类型的两个参数,不返回值BiPredicate<T, U> 接收T类型和U类型的两个参数,返回一个boolean类型的结果
6、java函数式接口源码分析
@FunctionalInterface
public interface Function<T, R> {
/**
* 唯一抽象方法
*/
R apply(T t);
/**
* 执行唯一的抽象方法前执行传入的函数式接口抽象方法
*/
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
/**
* 执行函数式接口抽象方法后,返回值作为传入的函数式入参
*/
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}其他函数式接口大同小异,直接查看其他函数式源码接口,本来是打算用jad反编译看看jvm对函数式接口的动态原理是什么,但是jad编译不成功,猜测jvm对赋值给函数式接口的方法(不管是lambal,还是构造函数,还是普通方法),感觉内部jvm会动态生成函数式接口的实现类,然后调用传入的方法。