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

类名::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

0%