Functional Interface- java8引入特性

为什么要了解

在学习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();
    }
}


@FunctionalInterface
interface 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/消费者接口有两个方法: void accept( T t) 和  default Consumer< T > andThen(Consumer<? super T > 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唯一抽象方法是 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));
}
来指定了顺序执行apply类型逻辑。
还有一个不知道为什么存在的方法(原样返回,用于复制?!)
static <T> Function<T, T> identity() {
    return t -> t;
}





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





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值