前身 -- 匿名类
何为匿名类?
没有类名,直接通过new关键字创建这个类的实例
比如:Comparator接口
这是一个单抽象方法的接口,这样的接口被称为函数式接口
public interface Comparator<T> {
int compare(T o1, T o2);
}
- 正常情况下,想使用
compare()
方法需要创建一个类实现这个接口
public class C implements Comparator<Integer> {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.sort(new C()); // 在这里传入这个实现类
System.out.println(list); // [2,1]
}
public int compare(Integer a, Integer b) {
return Integer.compare(b, a); // 返回 b - a 的比较结果
}
}
- 使用匿名内部类创建实例可以简化
public static void test() {
Comparator<Integer> c = new Comparator<Integer>() {
public int compare(Integer a, Integer b) {
return Integer.compare(b, a);
}
};
List<Integer> lst = new ArrayList<>();
lst.add(1);
lst.add(2);
lst.sort(c);
System.out.println(lst); // [2,1]
}
- 再简化
List<Integer> lst = new ArrayList<>();
lst.add(1);
lst.add(2);
// 策略模式,可以学习这种思想,自定义排序
lst.sort(new Comparator<Integer>() {
public int compare(Integer a, Integer b) {
return Integer.compare(b, a);
}
});
System.out.println(lst); // [2,1]
lambda 表达式
前面使用匿名类还是冗长,因此又创建了更简洁的方式来实现接口
List<Integer> lst = new ArrayList<>();
lst.add(1);
lst.add(2);
lst.sort((Integer a, Integer b) -> {return Integer.compare(b, a);});
System.out.println(lst);
本质是对匿名类的简写,只留下了这个 唯一抽象方法 的 参数列表 和 功能实现逻辑
lambda表达式箭头左侧中的参数列表 对应的是 接口中抽象方法的参数列表,右侧是对抽象方法需要实现的功能
- 可以再简化
List<Integer> lst = new ArrayList<>();
lst.add(1);
lst.add(2);
lst.sort((a, b) -> Integer.compare(b, a));
System.out.println(lst);
把多余的全去了,只留下必要的
其实我在好奇,把它简化为这样子,编译器怎么知道它是个Comparator<? super E>
这样的类型呢?
根据GPT的解答:
Java 编译器会根据上下文推断出该 Lambda 表达式的类型。因为 sort 方法期望一个 Comparator 对象作为参数,所以编译器会推断出这个 Lambda 表达式的类型是 Comparator 类型
额,总结一下就是,因为sort需要一个这样类型的参数,所以我就推断它就是一个这样的参数
方法引用
方法引用可以看出是lambda表达式的另外一种表现形式,也可以看成是对lambda的再次简化,思想类似于借花献佛、借鸡下蛋、草船借箭、借刀杀人....
怎么实现的呢?
如果有一个类/实例的方法和我这个lambda表达式中要实现的功能逻辑是一样的,那我能不能直接借用呢?
直接借用的话,至少需要知道这个类/实例和对应的方法
有三种实现方式:类名::静态方法,类名::实例方法,对象::实例方法 (其实思想基本一致)
类名::静态方法
首先,我需要根据一个list中元素大小按升序排列可以这么写:lst.sort((a, b) -> b - a);
,
List<Integer> lst = new ArrayList<>();
lst.add(1);
lst.add(2);
lst.sort((a, b) -> a - b);
System.out.println(lst); // [1,2]
a-b会返回-1、0、1这三种int值的其中一种,这个逻辑在Integer类的compara方法已经有实现了,所以可以直接引用Integer类的compara方法
// Integer.class
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
(前面也有用过,只是那是在功能逻辑中调用 Integer.compare()
)
List<Integer> lst = new ArrayList<>();
lst.add(1);
lst.add(2);
lst.sort(Integer :: compare);
System.out.println(lst);
类名::实例方法
除了使用compare方法可以实现List排序以外,compareTo方法也可以,它是Integer.class中的一个实例方法,调用的compare
// Integer.class
public int compareTo(Integer anotherInteger) {
return compare(this.value, anotherInteger.value);
}
使用lambda方式:
lst.sort((a, b) -> a.compareTo(b));
使用方法引用方式:
lst.sort(Integer :: compareTo);
对象::实例方法
forEach中接收一个函数式接口Consumer<? super T> action
,并调用了这个接口的抽象方法
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
如果使用lambda来打印列表中的元素的话,可以这么写:
List<Integer> lst = new ArrayList<>();
lst.add(1);
lst.add(2);
lst.forEach(a -> System.out.println(a));
这里和上一个例子逻辑一样,既然是在功能逻辑中调用了System.out的println方法,那肯定也能直接引用它们
这里使用System类中静态成员对象out来解释,out是一个PrintStream类型的对象,PrintStream这个类有一个println方法
// System.class
public final static PrintStream out = null;
最终简化为:
List<Integer> lst = new ArrayList<>();
lst.add(1);
lst.add(2);
lst.forEach(System.out::println);
总结
最后浅谈下我的理解
所谓的lambda表达式就是为简化代码而生,专为函数式接口(单个抽象方法的接口)服务
从 最初的匿名类的形式,即new Obj(){...某个方法...}
到 () -> {}
的形式,再到::
的形式,格式越来越简单
- 从
new Obj(){...某个方法...}
到() -> {}
的形式本质上是抽取了关键部分,因为人家调用方法时只需要直到方法的形参和实现逻辑就可以了,所以去掉了其余部分 - 从
() -> {}
的形式,再到::
这个过程的思想类似于借花献佛,如果这个方法的实现逻辑在某个类里有相同的实现逻辑,那我把这个类和方法借来一用可好?
参考资料
https://zhuanlan.zhihu.com/p/365505945
https://www.bilibili.com/video/BV1ci4y1g7qD?p=7&vd_source=885d19c94243993175e057fd4d9dc13c