@FunctionalInterface、Lambda表达式和方法引用

知识不回顾是会被遗忘的!

网上看了一些相关文章,这里记录一下,仅供参考

Java语言从JDK1.8开始引入了函数式编程。

        函数式编程的核心特点是,函数作为一段功能代码,可以像变量一样进行引用和传递,以便在有需要的时候进行调用。

1. @FunctionalInterface与“函数类型”

        Java对函数式编程的支持,本质是通过接口机制来实现的。首先定义一个仅声明一个方法的接口,然后对接口冠以@FunctionalInterface注解,那么这个接口就可以作为“函数类型”,可以接收一段以Lambda表达式,或者方法引用予以承载的逻辑代码。例如:

@FunctionalInterface
interface IntAdder {
    int add(int x, int y);
}

IntAdder adder = (x, y) -> x + y;

IntAdder 就可以看成是一个“函数类型”。Lambda表达式和方法引用的介绍见后文。

概念如此,需要思考的有几点:

为什么必须是只声明一个方法的接口?
        显然这个方法就是用来代表“函数类型”所能执行的功能,一个函数一旦定义好,它能执行的功能是确定的,就是调用和不调用的区别。接口中声明的方法就是和函数体定义一一对应的。
        事实上,@FunctionalInterface下只能声明一个方法,多一个、少一个都不能编译通过 。覆写Object中toString/equals的方法不受此个数限制。

比如Comparator接口就声明了2个方法:

// Comparator.java
@FunctionalInterface
public interface Comparator<T> {
    int compare(T o1, T o2);
    boolean equals(Object obj);
    //...
}

        严格地说,@FunctionalInterface下只能声明一个未实现的方法,default方法和static方法因为带有实现体,所有不受此限制。

  @FunctionalInterface
   public interface IAdd<T, R> {
       R add(T t1, T t2);
       
       default R test1(T t1, T t2) {//可以额外定义default方法
           return null;
       }
       
       static <T,R> R test2(T t1, T t2) {//可以额外定义static方法
           return null;
       }
   }

        关于interface中声明default/static方法有疑虑的话,可以查阅博主另一篇文章:java接口里面可以有成员变量么?

        @FunctionalInterface注解不是必须的,不加这个注解的接口(前提是只包含一个方法)一样可以作为函数类型。不过,显而易见的是,加了这个注解表意更明确、更直观,是更被推荐的做法。
        要定义清楚一个函数类型,除了函数名称,必须明确规定函数的参数个数和类型、返回值类型,这些信息都是包含于接口中声明的方法。


2. JDK提供的“函数类型”

java.util.function包下预定义了常用的函数类型,包括:

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t); //接收一个类型为T(泛型)的参数,无返回值;所以叫消费者
}
@FunctionalInterface
public interface BiConsumer<T, U> {
    void accept(T t, U u);//接收2个参数,无返回值
}
@FunctionalInterface
public interface Supplier<T> {
    T get();//无参数,有返回值(所以叫提供者)
}
//注意没有BiSupplier,因为返回值只能有1个,不会有2个
@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);//一个输入(参数),一个输出(返回值)
}
@FunctionalInterface
public interface BiFunction<T, U, R> {
    R apply(T t, U u);//两个输入T和U,一个输出R
}
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
    static <T> UnaryOperator<T> identity() {//一元操作,输入原样返回给输出
        return t -> t;
    }
}
@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {//二元操作,输入输出类型相同
    public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;//传入比较器,返回较小者
    }
    public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;//传入比较器,返回较大者
    }
}

        这些个定义,都是在参数个数(0,1,2)和有无返回值上做文章。另外还有一些将泛型类型具体化的衍生接口,比如Predicate、LongSupplier等等。

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);//输入1个参数,返回boolean,就好比是预言家,预言你这个输入是真还是假
}
@FunctionalInterface
public interface LongSupplier {
    long getAsLong();//没有输入,输出long类型(long类型的提供者)
}

3. Lambda表达式

        上面弄清楚了函数类型@FunctionalInterface,那么函数类型能接收怎么样的函数实现体呢?怎么接收呢?该Lambda出场了。

        Lambda表达式能赋值给一个变量,也就能当作参数传给函数。这个Lambda形式的变量/参数的类型是它所实现的那个接口,所包含的方法体便是这个接口抽象方法的实现。以后看到调用方法的参数是一个SAM类型接口的时候就可以考虑使用Lambda表达式替换匿名内部类来写。

作用:

  • ①减少代码量,突出代码意图
  • ②对集合数据 Collection 操作更简便
  • ③使变量记住一段逻辑:

        任务逻辑传递(传递一段运算逻辑给执行者)

        回调逻辑传递(简化接口回调的时候 new匿名类后实现抽象方法的模版代码)

将一个方法写成Lambda表达式,只需要关注参数列表和方法体。        

语法组成:

(参数类型 参数名) -> {
        方法体;

        return 返回值;

}

  • ①形式参数:最前面的部分是一对括号,里面是参数,无参数就是一对空括号
  • ②箭头:中间的是 -> ,用来分割参数和body部分,读作“ goes to”
  • ③方法体:body部分可以是一
  • 19
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值