Lambda 表达式

目录

一、Lambda 表达式介绍

二、Lambda表达式语法

1、语法

2、类型推断

3、变量作用域 


一、Lambda 表达式介绍

Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。

Lambda 表达式:在Java 8 语言中引入的一种新的语法元素和操作符。这个操作符为 “->” , 该操作符被称为 Lambda 操作符或箭头操作符。它将 Lambda 分为两个部分:

  • 左边:lambda形参列表 (其实就是接口中的抽象方法的形参列表)
  • 右边:Lambda体 (其实就是重写的抽象方法的方法体)

Lambda表达式的本质:作为函数式接口的实例

如果一个接口中,只声明了一个抽象方法,则此接口称为函数式接口,我们可以在一个接口上使用 @FunctionalInterface 注解 ,这样做可以检查它是否是一个函数式接口。

所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。

二、Lambda表达式语法

1、语法

语法格式一:无参,无返回值

    @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));
    }

2、类型推断

上述 Lambda 表达式中的参数类型都是由编译器推断得出的。Lambda表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的“类型推断”。

Lambda 表达式_第1张图片

3、变量作用域 

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 的语义)

Lambda 表达式_第2张图片

报错信息: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());  //编译会出错 

 参考文章:为什么局部内部类和匿名内部类只能访问 final 的局部变量? | 菜鸟教程 (runoob.com)

你可能感兴趣的:(Java基础,java,Lambda)