Lambda表达式
...大约 3 分钟
Lambda表达式
公式
//方法体是多行
()-> {
<语句>;
}
//方法体内是单行,语句后不需要分号
()-><语句>
@FunctionalInterface注解
- 这是一个标志注解,被该注解修饰的接口只能声明一个抽象方法
练习1 线程创建
public class LambdaLearn {
public static void main(String[] args) {
new Thread(()-> System.out.println("hello")).start();
}
}
练习2 编写接口
public class LambdaLearn {
public static void main(String[] args) {
speak(()-> {
System.out.println("用户1说话");
System.out.println("用户1一直说");
});
speak(()-> System.out.println("用户2说话"));
}
public static void speak(Human human){
human.speak();
}
}
@FunctionalInterface
interface Human{
public void speak();
}
练习3 排序
public class LambdaLearn {
public static void main(String[] args) {
List<Person> list = new ArrayList<>();
list.add(new Person("周杰伦", 33, 175));
list.add(new Person("刘德华", 43, 185));
list.add(new Person("周星驰", 38, 177));
list.add(new Person("郭富城", 23, 170));
//按照身高逆序
Collections.sort(list,(p1,p2)-> p2.getAge()-p1.getAge());
for (Person person : list) {
System.out.println(person);
}
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Person {
private String name;
private int age;
private Integer height;
}

/*
sort排序参数中的Comparator接口:
有两个抽象方法是因为:
equals() 方法并不算是函数式接口的一部分。在Java中,Object 类中的 equals() 方法是一个非常特殊的方法,它用于比较对象的相等性。因为 equals() 方法是 Object 类的一个公共方法,所以它并不会影响接口是否被标记为函数式接口。
*/
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);//公共方法,不影响注解
}
省略写法
小括号内的参数类型可以省略
如果小括号内有且仅有一个参数,则小括号可以省略
如果大括号内有且仅有一个语句,可以同时省略大括号,return 关键字及语句分号。
内部原理
匿名内部类在编译的时候会产生一个class文件。
Lambda表达式在程序运行的时候会形成一个类。
在类中新增了一个方法,这个方法的方法体就是Lambda表达式中的代码
还会形成一个匿名内部类,实现接口,重写抽象方法
在接口中重写方法会调用新生成的方法
使用前提
Lambda表达式的语法是非常简洁的,但是Lambda表达式不是随便使用的,使用时有几个条件要特别注意
方法的参数或局部变量类型必须为接口才能使用Lambda
接口中有且仅有一个抽象方法(@FunctionalInterface)
与匿名内部类的对比
- 所需类型不一样
匿名内部类的类型可以是 类,抽象类,接口
Lambda表达式需要的类型必须是接口
- 抽象方法的数量不一样
匿名内部类所需的接口中的抽象方法的数量是随意的
Lambda表达式所需的接口中只能有一个抽象方法
- 实现原理不一样
匿名内部类是在编译后形成一个class
Lambda表达式是在程序运行的时候动态生成class
作用域
访问局部变量
可以直接在 lambda 表达式中访问外部的局部变量:
final int num = 1;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
stringConverter.convert(2); // 3
但是和匿名对象不同的是,这里的变量 num 可以不用声明为 final,该代码同样正确:
int num = 1;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
stringConverter.convert(2); // 3
不过这里的 num 必须不可被后面的代码修改(即隐性的具有 final 的语义),例如下面的就无法编译:
int num = 1;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
num = 3;//在lambda表达式中试图修改num同样是不允许的。