0%

Java 8之方法引用

Java 8开始支持Lambda表达式,其避免了我们创建匿名内部类的麻烦,形式上更加优雅简洁易读。而如果我们Lambda表达式中只是调用一个已经封装过的方法,则显得过于繁琐、不够简约,为此Java 8在支持Lambda表达式的同时,也提供了一个语法糖:方法引用

abstract.jpeg

方法引用

方法引用有如下4种使用形式:

  1. 类名::静态方法名
  2. 对象实例名::实例方法名
  3. 类名::实例方法名
  4. 类名::new

类名::静态方法名

对于静态方法,通过类名::静态方法名的方式,即可调用该方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    public static void testMethodRef1() {
Set<Integer> set = new HashSet<>();
set.add(-12);
set.add(3);
set.add(-8);
set.add(4);
// 方式1: Lambda 表达式
System.out.println("------------ Lambda Expression ------------");
set.forEach( (e) -> {
MethodRefTest.getNum(e);
} );
// 方式2: 方法引用
System.out.println("\r\n------------ Method Reference ------------");
set.forEach( MethodRefTest::getNum );
}

public static void getNum(Integer a) {
Integer num = Math.abs(a);
System.out.println("num: " + num);
}

测试结果如下,表明上述两种形式作用是一致的,函数式接口中的参数全部传入所指定的静态方法中

figure 1.jpeg

对象实例名::实例方法名

对于实例方法,可以通过 对象实例名::实例方法名 的方式来使用,代码如下所示:

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
32
33
34
35
36
37
38
39
40
41
42
public class MethodRefTest {
public static void testMethodRef2() {
Set<Student> set = new HashSet<>();
for(int i=0; i<2; i++) {
Student student = new Student();
student.setAge(i);
student.setId(i);
student.setName(String.valueOf(i));
set.add(student);
}

Student stu = new Student();
stu.setName("Aaron");
stu.setId(78);
stu.setAge(24);

// 方式1: Lambda 表达式
System.out.println("------------ Lambda Expression ------------");
set.forEach( (e) -> {
stu.getDetail(e);
} );
// 方式2: 方法引用
System.out.println("\r\n------------ Method Reference ------------");
set.forEach(stu::getDetail);
}
}

@Data
class Student{
private Integer age;
private Integer id;
private String name;

public void getDetail() {
System.out.println("call: " + this);
}

public void getDetail(Student s) {
System.out.println("call: " + this);
System.out.println("param: " + s);
}
}

测试结果如下,表明上述两种形式作用是一致的。函数式接口中的参数全部传入所指定的实例方法中,而实例方法的调用者即为在lambda表达式和方法引用中指定的实例对象

figure 2.jpeg

类名::实例方法名

对于实例方法,其实还可以通过 类名::实例方法名 的方式来使用,代码如下所示:

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
32
33
34
35
36
37
38
39
40
41
42
43
public class MethodRefTest2 {

public static void testMethodRef3() {
Set<Student> set = new HashSet<>();
for(int i=0; i<2; i++) {
Student student = new Student();
student.setAge(i);
student.setId(i);
student.setName(String.valueOf(i));
set.add(student);
}

Student stu = new Student();
stu.setName("Aaron");
stu.setId(78);
stu.setAge(24);

// 方式1: Lambda 表达式
System.out.println("------------ Lambda Expression ------------");
set.forEach( (e) -> {
e.getDetail();
} );
// 方式2: 方法引用
System.out.println("\r\n------------ Method Reference ------------");
set.forEach(Student::getDetail);
}
}

@Data
class Student{
private Integer age;
private Integer id;
private String name;

public void getDetail() {
System.out.println("call: " + this);
}

public void getDetail(Student stu) {
System.out.println("call: " + this);
System.out.println("param: " + stu);
}
}

测试结果如下,表明上述两种形式作用是一致的,此时在方法引用中函数式接口所传入的第一个参数即为实例方法的调用者,如果有剩余参数的话,其将全部传入所指定的实例方法中

figure 3.png

上述三种方法引用与相应lambda表达式的对应关系,总结如下

figure 5.png

类名::new

对于构造器,可通过 类名::new 的方式来调用,代码如下所示:

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
public static void testMethodRef4() {
String[] array = new String[]{"Aaron", "Bob", "Tony"};
List<String> list = Arrays.asList(array);

System.out.println("------------ Lambda Expression ------------");
List<Student> students1 = list.stream()
.map(str -> new Student(str) ) // 通过lambda
.collect(Collectors.toList());
students1.forEach(System.out::println);

System.out.println("------------ Method Reference ------------");
List<Student> students2 = list.stream()
.map(Student::new) // 方法引用
.collect(Collectors.toList());
students2.forEach(System.out::println);
}

@Data
@NoArgsConstructor
@AllArgsConstructor
class Student{
private Integer age;
private Integer id;
private String name;

public Student(String name) {
this.name = name;
}
}

测试结果如下,表明上述两种形式作用是一致的:

figure 4.png

参考文献

  1. Java实战·第2版 拉乌尔-加布里埃尔·乌尔玛、马里奥·富斯科、艾伦·米克罗夫特著
请我喝杯咖啡捏~
  • 本文作者: Aaron Zhu
  • 本文链接: https://xyzghio.xyz/MethodRef/
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-ND 许可协议。转载请注明出处!

欢迎关注我的微信公众号:青灯抽丝