Functional Interface- java8引入特性

来源:互联网 发布:java设置test字体大小 编辑:程序博客网 时间:2024/06/10 19:38

为什么要了解

在学习Lambda表达式和Optional类的使用过程中,发现多涉及到java.util.function包下边的接口。因此猜想了解函数式接口是学习Lambda表达式的基础,或者是深入学习javaf8的必经之路。本篇博客仅限抽象理解,知道其基本概念,详情请参考后续博客或其他资料详情。

简介

function包下接口称为函数式接口,又称SAM(Single Abstract Method Interface),如名、函数式接口中的抽象方法只能有一个,但是:
  1. 可以包含多个来自Object类的public方法,因为所有接口或者类都默认继承Object类,包含对Object方法的实现;
  2. (函数式)接口允许存在default方法静态方法,接口中引入静态方法和default方法都是java 8引入的新特性,之前不允许存在;
    @FunctionalInterface//使用此注解,编译器会扫描此接口,确认只有一个抽象方法,否则会报错public interface FunctionalTest {    /**     * 唯一的一个抽象方法     */    void onlyAbsMethod();//    void secAbsMethod();添加此行代码后会提示错误;    /**     * 继承Object的抽象方法是OK的,不会报错,     * 因为所有接口和类都默认继承Object类,不违反唯一抽象规则;     */    @Override    boolean equals(Object obj);    /**default方法     */    default void defaultMethod(){        System.out.println("method can exist in funcIntercace");    }    /**静态方法     */    public static void staticMehotd(){        System.out.println("static method can exist in funcInterface");    }

用途

Lambda可以表示函数式接口的一个实现,示例:
public class BlogTest {    public static void main(String[] args) {        //Lambda表达式表示函数式接口的实现,即返回一个接口的“实例”;        //重点 TODO :Lambda表达式参数个数和类型与接口中唯一抽象方法对齐        FuncInterface funcInterface=(x,y)-> System.out.println(x+y);        //调用        funcInterface.onlyAbsMethod("du","genkui");        //java 8接口引入了default方法和静态方法,静态方法只能用接口名称调用        funcInterface.defaultMethod();        FuncInterface.staticMethod();    }}@FunctionalInterfaceinterface FuncInterface{    void onlyAbsMethod(String param1,String param2);    default void defaultMethod(){        System.out.println("default in interface,java 8");    }    static void staticMethod(){        System.out.println("static methid in interface.java 8。can be invoked by InterfaceName");    }}

java 8中的函数式接口

在java 8中,之前很多接口都加上了@FunctionalInterface,以函数式接口的形态存在——这些接口都可以使用Lambda表达式表示其一个实例,而不用再使用内部类了;
  1. Runnable在java 8中变成了函数式接口,注意用Lambda表达式实现时Lambda表达式参数个数和类型与Runnable中唯一抽象方法对齐
            new Thread(new Runnable() {            @Override            public void run() {                System.out.println("implements before java 8 using anonymous inner Class");            }        }).start();        /**         *  java 8中,Lambda表达式对Runnable接口的实现,点进去可以看见相应构造器         *  TODO 重点:         *      1.可以这样实现的原因是Runnable接口用@FunctionalInterface注释;         *      2.TODO Lambda表达式中的参数个数了类型对应FunctionalInterface 接口唯一抽象方法的参数个数及类型         */        new Thread(()-> System.out.println("implements in java 8")).start();        //new Thread((x)-> System.out.println("implements in java 8"));报错,因为new Runnable.run()方法没有参数
疑问,如果Thread中有两个构造器参数都是@FunctionalInterface接口,用Lambda表达式实现时,会调用那个呢,情况看(java比我想象聪明一万倍啊)
两个FunctionalInterface接口的唯一抽象类参数个数相同和不同的情况下,参数类型相同和不同的情况下:
  • 参数个数不同时,根据更具Lambda表达式参数个数对应到不同的函数式接口类,在对应到不同的构造器;
  • 参数个数相同时,更具Lambda表达式参数类型对应到不同的...
综上,用Lambda初始化一个参数类型为函数式接口的类型实例时,Lambda表达式参数个数和类型对应到不同的函数式接口,在对应到不同的构造器;

@FunctionalInterfacepublic interface Runnable { public abstract void run();}
public class FunctionalClass { private FunctionaX functionaX; private FunctionaY functionaY; private FunctionaZ functionaZ; public FunctionalClass(FunctionaX functionaX) { this.functionaX = functionaX; } public FunctionalClass(FunctionaY functionaY) { this.functionaY = functionaY; } public FunctionalClass(FunctionaZ functionaZ) { this.functionaZ = functionaZ; } public static void main(String[] args) { new FunctionalClass((String x)-> System.out.println("test")); new FunctionalClass((x,y)-> System.out.println(x+y)); new FunctionalClass((int x)-> System.out.println("test")); //TODO 会报错,因为唯一抽象方法只有一个的构造器参数有两个,编译器不知道该调用那个,必须指定类型; // TODO 但是两个参数类型一样时,指定类型也会报错 // new FunctionalClass((x)-> System.out.println("test")); }}@FunctionalInterfaceinterface FunctionaX{ void onlyAbsMethod(String mes);}@FunctionalInterfaceinterface FunctionaY { void onlyAbsMethod(String mes,String mess);}@FunctionalInterfaceinterface FunctionaZ{ void onlyAbsMethod(int mes);}


2.Callable<V>:Callable<V>和Future经常混合使用,两者都是用了泛型,后者不是函数式接口,稍后学习多线程内容是结合一块讲解

@FunctionalInterface
public interface Callable<V> { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception;}

3.java.util.function包下的函数式接口,常用的有Consumer、Predicate个 还有Function;Optional类的实现对着三个多有涉及;
Consumer,如命为“消费者”,被Optional的ifPresent(XX)调用。此函数的作用是如果Optional中有元素,则调用Consumer消费者类处理此元素。由于Consumer消费者是一个函数式接口,因此可以用Lambda表达式返回其一个实例,注意表达式与消费者接口唯一抽象方法参数个数和类型对齐。源码如下
//value为Optional保存的元素,也是消费者接口唯一抽象方法的入参,也是Lambda式子的参数    public void ifPresent(Consumer<? super T> consumer) {        if (value != null)            consumer.accept(value);    }
ifPresent()函数中的value值是Optional保存的元素,也是消费者接口唯一抽象方法的入参(由此可知用到了泛型),也是声明消费者Consumer接口“实例”时Lambda表达式的参数
4.Consumer/消费者接口有两个方法:voidaccept(Tt) 和 defaultConsumer<T> andThen(Consumer<?superT> after){},前者是唯一抽象方法,Lambda表达式参数应与其对齐;前者是处理参数,后者指作为参数可以再Consumer的accept方法处理完入参后再处理一次。其源码和相关注释讲解如下
    /**     * Lambda表达式表示处理Optional元素的逻辑     */    void accept(T t);    /**     * @param after 在accpet(T t)处理完参数后的第二个逻辑,继续对参数进行处理     */    default java.util.function.Consumer<T> andThen(java.util.function.Consumer<? super T> after) {        Objects.requireNonNull(after);        return (T t) -> {            accept(t); //首先进行此Consumer类的处理逻辑;            after.accept(t);//然后anThen(Consumer X)中参数逻辑在对参数进行处理        };    }
如下示例为使用两个方法,注意Iterator的forEachRemaining()方法1.使用函数式接口处理其中元素,而且Iterator遍历完之后不能重置;
        //将一个字符链表装如Optional中        Optional<Iterator<String>> optiExist= Optional.of((Arrays.asList("du","gen","kui").iterator()));        //没元素        Optional optEmp= Optional.empty();        //测试Consummer的两个方法        optiExist.ifPresent(x->{            System.out.println(x.getClass());            System.out.println("测试{}符号");} );        optEmp.ifPresent(x-> {                System.out.println(x.getClass());                 System.out.println("测试{}符号");        });        //测试AndThen方法:TODO 重点:在Consummer<>尖括号中指定Lambda表达式、也是抽象方法参数的类型        Consumer<Iterator<String>> consumer=x->x.forEachRemaining( y->System.out.println(y+"X"));        optiExist.ifPresent(consumer.andThen(                x-> System.out.println("在遍历元素之后调用"))        );
//补充   forEachRemaining源码  default void forEachRemaining(Consumer<? super E> action) {        Objects.requireNonNull(action);        while (hasNext())            action.accept(next());    }


5.Predicte,如名“断言”,唯一抽象方法test(T t);返回值是Boolean类型,主要用于检查元素是否符合某些条件。还可以用or、and连接条件,也可以用
Predicate<T> negate() 方法取其相反断言。代码示例如下;
        //测试使用链表        List<Integer> arrayList=new ArrayList<>();        arrayList.add(-1);        arrayList.add(0);        arrayList.add(1);        //检测Integrate类型元素与0的关系;        Predicate<Integer> bigThanZero=x->x>0;        Predicate<Integer> equalZero=x->x==0;        //过滤掉链表中大于0的数打印,然后测试过滤掉不大于0的数(注意不是小于),然后打印        arrayList.stream().filter(bigThanZero).forEach(a-> System.out.print(a));        System.out.println();        arrayList.stream().filter(bigThanZero.negate()).forEach(x-> System.out.print(x+","));        System.out.println();        //or和and        arrayList.stream().filter(bigThanZero.or(equalZero)).forEach(a-> System.out.print(a+","));//大于或者等于0        System.out.println();        arrayList.stream().filter(bigThanZero.negate().and(equalZero)).forEach(a->System.out.println(a+","));//不大于且等于0,只有0
        
    /** isEqual(Object)源码分析,使用到了泛型,注意其返回值类型     * @param targetRef 对比的对象     * @return 返回一个Lambda表达式,表示对Predicate的初始化     */    static <T> Predicate<T> isEqual(Object targetRef) {        return (null == targetRef)                ? Objects::isNull//等价于 obj->Object.isNull(obj);                : object -> targetRef.equals(object);    }
 //测试代码:内层的(Predicate.isEqual(1) 返回Predicate实例: x->x.equals(1);        arrayList.stream().filter(Predicate.isEqual(1)).forEach(x-> System.out.println(x+" equal"));



Function唯一抽象方法是Rapply(Tt);

还有
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));}
来指定了顺序执行apply类型逻辑。
还有一个不知道为什么存在的方法(原样返回,用于复制?!)
static <T> Function<T, T> identity() {    return t -> t;}





Lambda表达式可以是多句,用中括号括起来,如
x->{expressionX;expressionB;}