目录
Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
Lambda 表达式:在Java 8 语言中引入的一种新的语法元素和操作符。这个操作符为 “->” , 该操作符被称为 Lambda 操作符或箭头操作符。它将 Lambda 分为两个部分:
Lambda表达式的本质:作为函数式接口的实例
如果一个接口中,只声明了一个抽象方法,则此接口称为函数式接口,我们可以在一个接口上使用 @FunctionalInterface 注解 ,这样做可以检查它是否是一个函数式接口。
所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。
语法格式一:无参,无返回值
- @Test
- public void test1(){
- Runnable r1 = new Runnable(){
- @Override
- public void run() {
- System.out.println("111111111");
- }
- };
- r1.run();
-
- Runnable r2 = () -> System.out.println("22222222222");
- r2.run();
- }
语法格式二:Lambda 需要一个参数,但是没有返回值。
- @Test
- public void test2(){
- Consumer
con1 = new Consumer(){ - @Override
- public void accept(String s) {
- System.out.println(s);
- }
- };
- con1.accept("123456789");
-
- Consumer
con2 = (String s) -> System.out.println(s); - con2.accept("123");
- }
语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
- @Test
- public void test3(){
- Consumer
con1 = (String s) -> System.out.println(s); - con1.accept("123");
-
- Consumer
con2 = (s) -> System.out.println(s); - con2.accept("123");
- }
语法格式四:Lambda 若只需要一个参数时,参数的小括号可以省略
- @Test
- public void test4(){
- Consumer
con1 = (s) -> System.out.println(s); - con1.accept("123");
-
- Consumer
con2 = s -> System.out.println(s); - con2.accept("123");
- }
语法格式五:Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值
- @Test
- public void test5(){
- Comparator
com1 = new Comparator() { - @Override
- public int compare(Integer o1, Integer o2) {
- System.out.println(o1);
- System.out.println(o2);
- return o1.compareTo(o2);
- }
- };
- System.out.println(com1.compare(12,34));
-
- Comparator
com2 = (o1,o2) ->{ - System.out.println(o1);
- System.out.println(o2);
- return o1.compareTo(o2);
- };
- System.out.println(com2.compare(23,45));
- }
语法格式六:当 Lambda 体只有一条语句时,return 与大括号若有,都可以省略
- @Test
- public void test6(){
- Comparator
com1 = new Comparator() { - @Override
- public int compare(Integer o1, Integer o2) {
- return o1.compareTo(o2);
- }
- };
- System.out.println(com1.compare(23,45));
-
- Comparator
com2 = (o1,o2) -> o1.compareTo(o2); - System.out.println(com2.compare(12,21));
- }
上述 Lambda 表达式中的参数类型都是由编译器推断得出的。Lambda表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的“类型推断”。

lambda 表达式只能引用标记了 final 的外层局部变量,这就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。
- public class LambdaTest3 {
- public static void main(String[] args) {
- final int num = 5;
- Consumer
con = (o) -> System.out.println(o + num); - con.accept(1);
- }
- }
lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义)

报错信息:Local variable num defined in an enclosing scope must be final or effectively
这句话的意思是lambda 表达式中使用的变量应该是 final 或者有效的 final
其实在 Java 8 之前,匿名类中如果要访问局部变量的话,那个局部变量必须显式的声明为 final,如下代码在 Java 7 中是编译不过的:
- public static void main(String[] args) {
- int num = 5;
- new Consumer
(){ - @Override
- public void accept(Integer integer) {
- System.out.println(integer + num);
- }
- };
- num = 8;
- }
我们知道,lambda 表达式是由匿名内部类演变过来的,它们的作用都是实现接口方法,于是类比匿名内部类,lambda 表达式中使用的变量也需要是 final 类型。
但是 num 并没有声明为 final 类型,然而代码却能够编译通过,这是因为 Java 8 之后,在内部类或 Lambda 表达式中访问的局部变量,如果不是 final 类型的话,编译器自动加上 final 修饰符,即 Java8 新特性:effectively final。
追究其根本原因就是作用域中变量的生命周期导致的,首先需要知道的一点是: 内部类和外部类是处于同一个级别的,内部类不会因为定义在方法中就会随着方法的执行完毕就被销毁。
这里就会产生问题:当外部类的方法结束时,局部变量就会被销毁了,但是内部类对象可能还存在(只有没有人再引用它时,才会死亡)。这里就出现了一个矛盾:内部类对象访问了一个不存在的变量。为了解决这个问题,就将局部变量复制了一份作为内部类的成员变量,这样当局部变量死亡后,内部类仍可以访问它,实际访问的是局部变量的"copy"。这样就好像延长了局部变量的生命周期。
问题又出现了:将局部变量复制为内部类的成员变量时,必须保证这两个变量是一样的,也就是如果我们在内部类中修改了成员变量,方法中的局部变量也得跟着改变,怎么解决问题呢?
就将局部变量设置为final,对它初始化后,我就不让你再去修改这个变量,就保证了内部类的成员变量和方法的局部变量的一致性。这实际上也是一种妥协。
在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量。
- String first = "";
- Comparator
comparator = (first, second) -> Integer.compare(first.length(),second.length()); //编译会出错