java8函数式接口

999 阅读4分钟

函数式接口的定义 

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会动态生成函数式接口的实现类,然后调用传入的方法。