我这里只是简单记录一下,让自己有个印象,如果想要详细的教程,那么可以看《Java 8实战》
lambda为简洁地表示可传递的匿名函数的一种方式。
它包含三个部分,参数、箭头、主体。如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值
List<Dog> dogs = new ArrayList<>();
(a1,a2)是参数
a1.getName().compareTo(a2.getName())是主体
dogs.sort((a1, a2) -> a1.getName().compareTo(a2.getName()));
函数式接口就是只定义一个抽象方法的接口。Lambda表达式允许你直接以内联的形式为函数式接口的抽象方法提供实现, 并把整个表达式作为函数式接口的实例、
例如:
Runnable r1 = () -> System.out.println("Hello World 1")
- 类型推断
Java编译器会从上下文(目标类型)推断出用什么函数式接口来配合Lambda表达式,这样就可以在Lambda语法中省去标注参数类型。
List<Dog> dogs = new ArrayList<>();
dogs.sort((Dog a1, Dog a2) -> a1.getName().compareTo(a2.getName()));
可以简写为
dogs.sort((a1, a2) -> a1.getName().compareTo(a2.getName()));
- lambda使用局部变量
Lambda表达式引用的局部变量必须是最终的( final)或事实上最终的(即没有再改过)。
#这种写法正确
int portNumber = 1337;
Runnable r = () -> System.out.println(portNumber);
# 再重新给portNumber 赋值会报错
int portNumber = 1337;
Runnable r = () -> System.out.println(portNumber);
portNumber = 3434;
为什么 Lambda 表达式(匿名类) 不能访问非 final 的局部变量呢?因为实例变量存在堆中,而局部变量是在栈上分配,Lambda 表达(匿名类) 会在另一个线程中执行。如果在线程中要直接访问一个局部变量,可能线程执行时该局部变量已经被销毁了,而 final 类型的局部变量在 Lambda 表达式(匿名类) 中其实是局部变量的一个拷贝。
- 方法引用
使用 方 法 引用时 , 目 标引用 放 在 分隔符::前 ,方法 的 名 称放在 后 面,例如Dog::getName就是引用了Dog类中定义的方法getName。
方法引用主要有三类。
- 指向静态方法的方法引用(例如Integer的parseInt方法,写作Integer::parseInt)。
- 指 向 任 意 类 型 实 例 方 法 的 方 法 引 用 ( 例 如 String 的 length 方 法 ,写 作String::length)。
- 指向现有对象的实例方法的方法引用(假设你有一个局部变量expensiveTransaction用于存放Transaction类型的对象,它支持实例方法getValue,那么你就可以写expensiveTransaction::getValue)
改造示例
例如需要将Dog列表,根据姓名排序,那么一开始可以这么写
List<Dog> dogs = new ArrayList<>();
dogs.sort(new Comparator<Dog>() {
@Override
public int compare(Dog o1, Dog o2) {
return o1.getName().compareTo(o2.getName());
}
});
- 使用lambda表达式
dogs.sort((Dog o1, Dog o2) -> o1.getName().compareTo(o2.getName()));
去掉类型判断后,变为
dogs.sort((o1, o2) -> o1.getName().compareTo(o2.getName()));
Comparator具有一个叫作comparing的静态辅助方法,所以还可以继续简写
dogs.sort(Comparator.comparing((a) -> a.getName()));
- 加入方法引用
dogs.sort(Comparator.comparing(Dog::getName));