4

lambda 表达式学习笔记 - bmilk

 1 year ago
source link: https://www.cnblogs.com/bmilk/p/16818999.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
  • 函数式接口
  • 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
// 给定一个值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的行为完全相同。

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

    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也都可以用于函数式编程


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK