Fork me on GitHub

方法引用拾遗

方法引用

方法引用是java8引入lambda表达式之后的一个特性,在日常的开发工作中经常使用,可以看做是一种函数指针(function pointer),也可以简单看做lambda表达式的一种语法糖。

方法引用的分类

可以将方法引用分为四种:

  1. 类::static方法
  2. 对象::实例方法
  3. 类::实例方法
  4. 构造函数引用

前两种应该很好理解,现在来简单记录下第三种。

类::实例方法的方法引用

这个看起来并不符合常规,类应该去调用static方法才对,为什么可以直接调用实例方法呢?以代码方式演示会比较好理解。

准备一个java bean。

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
public class Student {

String name;
Integer score;

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

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getScore() {
return score;
}

public void setScore(Integer score) {
this.score = score;
}

public int compareByName(final Student s2) {
return this.getName().compareToIgnoreCase(s2.getName());
}

public int compareByScore(final Student s2) {
return this.getScore() - s2.getScore();
}

@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}

可以看到这个Student类中有根据name和score进行比较的方法。

测试类::static方法这种方法引用方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private static void 类实例方法() {
Student s1 = new Student("张三", 21);
Student s2 = new Student("李四", 22);
Student s3 = new Student("王五", 23);


List<Student> students = Arrays.asList(s1, s2, s3);
students.sort(Student::compareByName); // 在实例方法内部compareByName处打了断点,会被调用两次(list中只有三个元素),
// List.sort方法接收参数是一个Comparator,而这个实例方法是返回int的。
// 这个说明 类::实例方法 这种方法引用是肯定要有一个对象去调用这个实例方法的。这个对象就是传入lambda表达式的list中的第一个Student对象 而后一个student对象会作为compareByName的参数传入


students.forEach(System.out::println);
}

可以看到这里students.sort(Student::compareByName);是可以直接传入compareByName方法的。List.sort方法的参数是个Comparator接口,而compareByName返回的是int类型,这里编译器也没有报错。

其实这行就是相当于另外一种写法的

students.sort((st1, st2) -> st1.compareByName(st2));

这里鼠标点击箭头,可以直接跳转到Comparator接口的,这里的类::实例方法,其实并不是指的是这个实例方法,而是对这个students数组中的每个元素去应用这个compareByName方法,而进行比较的是从数组的第一个元素调用compareByName方法,这里的参数就是第二个元素。

这里也可以打个断点去看到compareByName是被调用两次的。(list中有三个元素)

default方法彩蛋

默认方法也是java8新增的一个特性,用来解决接口向下(或者老版本)兼容的问题。

这里看到一个有趣的点:当两个接口中有同名默认方法,子类同时实现这两个接口,会出现什么情况呢?

1
2
3
4
5
6
public interface MyInterface1 {

default void myMethod() {
System.out.println("MyInterface1");
}
}
1
2
3
4
5
6
public interface MyInterface2 {

default void myMethod() {
System.out.println("MyInterface2");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class 同名默认方法测试 implements MyInterface1, MyInterface2 {

@Override
public void myMethod() {
// 指定实现MyInterface1中的myMethod方法
MyInterface1.super.myMethod();
}

public static void main(String[] args) {
同名默认方法测试 m = new 同名默认方法测试();

m.myMethod();
}

}

这里当没有指定实现的到底是哪个接口中的默认方法会编译不通过。(这类是用的一种特殊的写法:MyInterface1.super.myMethod()去指定对应的默认方法)。

-------------本文结束感谢您的阅读-------------

本文标题:方法引用拾遗

文章作者:夸克

发布时间:2019年12月09日 - 16:12

最后更新:2022年07月01日 - 06:07

原始链接:https://zhanglijun1217.github.io/2019/12/09/方法引用拾遗/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。