Java 8 中的方法引用

一、原理概要

lambda 表示式,可以作为某些匿名内部类的替代。主要目的是调用该内部类中的方法,而该方法的实现(重写)由 lambda表示式决定。

通常,我们可能不关心匿名内部类中的具体方法(被重写的方法),而只关心该方法是怎么被重写的(方法的实现)。因此,我们可以构造一个中间对象(通常是接口,比如 Funtion),该接口拥有一个需要该重写的方法(比如 Function 对应的方法是 apply)。

 

二、如何使用

在实际书写时,可以只写出(传递的参数)与{方法的实现},或者只标出实现过程的 调用者的和其方法名(使用双冒号分隔)。

1
2
3
Function<Person, Integer> getAge = Person::getAge;
// 传参数调用 getAge 方法
Integer age = getAge.apply(p);

 

注释:

1. 比如 Function<T,R>T 表示传入类型,R 表示返回类型。比如,表达式 person -> person.getAge();,传入参数是 person,返回值是 person.getAge(),那么方法引用 Person::getAge 就对应着 Function<Person,Integer> 类型。

 

2. 使用双冒号的方式会返回这个对象,当你调用该对象中的 apply 方法时,就会调用你之前传进去的方法。

 

3. 这个过程类似一个函数的闭包。当你使用双冒号的方式,会把你的类和方法传入并生成一个 Function对象。该对象会在之后某个时间被调用,此时它会调用之前你传入这个对象中的方法。

 

三、什么情况下可以使用方法引用(双冒号)?

lambda 的两种方式对比

1
2
3
4
5
6
7
8
9
10
11
// old way
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
for (Integer n : list) {
    System.out.println(n);
}
 
// 使用 -> 的 Lambda 表达式
list.forEach(n -> System.out.println(n));
 
// 使用 :: 的 Lambda 表达式
list.forEach(System.out::println);

 

list的 foreach 方法参数中有个函数式接口Consumer。该接口中有个抽象方法 accept能够接收一个参数但是没有返回值,这个时候我想实现accept方法,让它的功能为打印接收到的那个参数,那么我可以使用Lambda表达式这么做

1
2
Consumer<String> consumer = str -> System.out.println(str);
consumer.accept("This is Major Tom");

 

但是类似的功能PrintStream类(也就是System.out的类型)的println方法已经实现了,

1
2
Consumer<String> consumer = System.out::println;
consumer.accept("This is Major Tom");

 

这个类似之处就是 accept方法跟println方法都是接收一个参数类型为String参数,并且无返回值。

这就是方法引用的规定,实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!至于返回值就不作要求。

   

四、关于函数式接口

1. Java 8中允许接口实现方法, 而不是简单的声明。需要使用关键字 default

2. Java8 中的 函数式接口中除了定义抽象方法外还可以包含静态方法。

1
2
3
4
5
6
7
8
@FunctionalInterface
interface FunctionalInterfaceWithStaticMethod {
    static int sum(int[] array) {
        return Arrays.stream(array).reduce((a, b) -> a+b).getAsInt();
    }
     
    void apply();
}

注. @FunctionalInterface注解,主要用于编译级错误检查,加上该注解,当你写的接口不符合函数式接口定义的时候,编译器会报错。 

 

3.一般情况下函数式接口只会定义一个抽象方法,但是接口最终有确定的类实现, 而类的最终父类是Object。 因此函数式接口可以定义Object 中的public方法,protected 方法不行。

1
2
3
4
5
6
7
8
@FunctionalInterface
public interface ObjectMethodFunctionalInterface {
    void count(int i);
     
    String toString(); //same to Object.toString
    int hashCode(); //same to Object.hashCode
    boolean equals(Object obj); //same to Object.equals
}

  

4. util 包中的函数式接口

java.util.function中定义了几组类型的函数式接口以及针对基本数据类型的子接口。

  • Predicate -- 传入一个参数,返回一个bool结果, 方法为boolean test(T t)
  • Consumer -- 传入一个参数,无返回值,纯消费。 方法为void accept(T t)
  • Function<t,r> -- 传入一个参数,返回一个结果,方法为R apply(T t)
  • Supplier -- 无参数传入,返回一个结果,方法为T get()
  • UnaryOperator -- 一元操作符, 继承Function<t,t>,传入参数的类型和返回类型相同。
  • BinaryOperator -- 二元操作符, 传入的两个参数的类型和返回类型相同, 继承BiFunction

 

posted on   Lemo_wd  阅读(542)  评论(0编辑  收藏  举报

编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

点击右上角即可分享
微信分享提示