lambda 表达式学习笔记

时间:2022-10-23 18:06:35

目录

  • 函数式接口
  • lambda 表达式简介
    • 理解lambda表达式——匿名内部类
    • lambda表达式语法
    • lambda行为参数化
  • lambda表达式的作用域
    • this 和 super ,lambda表达式真不是匿名内部类
    • 变量作用域
  • java8+中的函数式接口

函数式接口

如果一个Java接口类包含且仅包含一个无默认实现的方法定义,那么他被称为函数式接口。这个方法定义了接口的预期用途。
@FunctionalInterface注解指示一个接口类是一个函数接口,不允许有多个没有默认实现的接口,但不强制要求在接口上添加该注解

示例1:定义一个函数式接口

@FunctionalInterface
public interface MyFunctionalInterface {
    // the single abstract method
    void function();
}

示例1中有且仅有没有默认实现的方法定义——function(), 因此它是一个功能接口。

lambda 表达式简介

理解lambda表达式——匿名内部类

lambda表达式本质上是一个匿名函数,用于实现一个函数接口定义的功能(不能理解的话可以看作是一个匿名对象,后面会专门说区别)。

lambda表达式语法

lambda表达式的一般语法是:

(Parameters) -> { Body }

Parameters为函数的入参,放在()中,与方法定义相同,lambda表达式的实现放在{}

注意

  • 参数的类型声明是可选的,编译器可以统一识别参数类型。明确声明参数类型的lambda表达式称为显示lambda表达式,反之称之为隐式lambda表达式
  • ()可选的,当参数有且仅有一个时,可以省略小括号,否则必须保留
  • {}可选的,如果实现的主题只有一条语句,则可以省略,反之必须保留
  • 返回关键字retuen可选的,在省略{}的同时,如果表达式有返回值可以省略关键字retuen

示例2:

// 给定一个值x,返回其的3倍
(int x) ->{return x*3;};
// 省略可选项后定义如下:
x -> x * 3;

lambda行为参数化

可以将lambda表达式作为参数传递给方,就像传递一个普通的对象一样
示例3:

public class Test {
    public static void main(String[] argv) {
        doWork((x,y)-> x + y, 4, 2);
    }

    // (x,y)-> x + y  即为Function 函数接口的实现
    private static int doWork(Function function, int x, int y){
        return function.cal(x,y);
    }

    // 定义一个函数接口, 对两个整形进行计算
    @FunctionalInterface
    interface Function{
        int cal(int x, int y);
    }
}

lambda表达式的作用域

this 和 super ,lambda表达式真不是匿名内部类

lambda表达式使用时不会生成新的对象,也就不会产生自己的范围。
如果lambda表达式中使用关键字thissuper,他的行为与在执行lambda表达式的方法中使用thissuper的行为完全相同。

示例4:

public class Test {
    public static void main(String[] argv) {
        Test test = new Test();
        test.test();
    }
    private void test(){
        MyFunctionalInterface functionNewClass = new MyFunctionalInterface(){
            @Override
            public void function(int x) {
                System.out.println("匿名内部类this=======   "+ this);
            }
        };
        MyFunctionalInterface functionLambda = x -> System.out.println("lambda表达式中执行this ==========  " + this);
        System.out.println("方法中执行this=======   "+ this);
        functionNewClass.function(1);
        functionLambda.function(1);
    }
}


// 执行结果
方法中执行this=======   cn.bmilk.Test@4bf558aa
匿名内部类this=======   cn.bmilk.Test$1@2d38eb89
lambda表达式中执行this ==========  cn.bmilk.Test@4bf558aa

示例3的结果显示,lambda表达式中调用this指针和在方法test()中调用this指代的是同一个对象cn.bmilk.Test@4bf558aa, 而使用匿名内部类this指代的是当前匿名内部类产生的对象cn.bmilk.Test$1@2d38eb89,所以lambda表达式和匿名内部类是不同的

变量作用域

lambda表达式可以使用`标记了`final的外层局部变量,这就是说在lambda表达式中不能修改外层定义的局部变量(不包含全局变量),否则会编译错误。

示例5:引用外层局部变量

    private void test(){
        int num = 2;
        MyFunctionalInterface functionLambda = x -> System.out.println(x * num);
        functionLambda.function(1);
    }

示例5:test*()中定义了一个变量num,并且在functionLambda中进行了引用。前面提到lambda表达式使用外层局部变量,外层局部变量需要定义为final,但这里并没又定义为final,这是因为num虽然没有被定义为final,但是在num被定义之后并没有再次改变,看起来和一个被final修饰的变量一样,即含有隐性的final定义。
当尝试再后面修改num的值,编译器会提示错误信息,如示例6

示例6:

    private void test(){
        int num = 2;
        num = 3;
        // 编译器提示 引用外层局部变量,外层局部变量必须为final或者等同于final
        // Variable used in lambda expression should be final or effectively final
        MyFunctionalInterface functionLambda = x -> System.out.println(x * num);
        functionLambda.function(1);
    }

之所以引用外层变量必须是final是因为,局部变量被定义再栈帧中,java访问局部变量的时候实际上是访问他的副本,多个线程对应了多个栈,当lambda表达式执行线程与定义线程不是同一个情况下,如果变量不被定义为final,当一个线程改变了变量值,另一个有可能会读取不到最新值导致出错。

java8+中的函数式接口

java8及更高版本内置了一系列的函数式接口,被放在java.util.function包下,用于支持j函数式编程。另外在之前的版本中存在的java.lang.Runnablejava.util.Comparator也都可以用于函数式编程