为什么引入Lambda表达式
在Java中传递一个代码段不容易,不能直接传递代码段。Java是一种面向对象语言,必须构造一个对象,这个对象的类需要有一个方法能包含所需的代码。
Lambda表达式是一个可传递对的代码块,可以在以后执行一次或多次。
Lambda表达式的语法
(parameters)->expression或(parameters)->{statements}
以下是Lambda表达式的重要特征:
- 可选类型说明:不需要声明参数类型,编译器可以统一识别参数值。
- 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号(如果没有参数,需要有圆括号)。
- 可选的大括号:如果主体包含了一个语句,就不需要大括号。
- 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回一个数值。
函数式接口
对于只有一个抽象方法的接口,需要这种接口的对象时,就可以提供一个lambda表达式,这种接口称为函数式接口。
为什么函数接口必须要有一个抽象方法呢?是因为接口完全有可能重新声明Object类的方法,这些声明有可能让方法不再是抽象的。
在java 8中已经为我们定义了很多常用的函数式接口它们都放在java.util.function包下面,一般有以下常用的四大核心接口:
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer<T>(消费型接口) | T | void | 对类型为T的对象应用操作。void accept(T t) |
Supplier<T>(供给型接口) | 无 | T | 返回类型为T的对象。 T get(); |
Function<T, R>(函数型接口) | T | R | 对类型为T的对象应用操作并返回R类型的对象。R apply(T t); |
Predicate<T>(断言型接口) | T | boolean | 确定类型为T的对象是否满足约束。boolean test(T t); |
在任意函数式接口上使用@FuctionalInterface注解,这样可以检测它是否是一个函数式接口,同时javadoc会包含一条声明,说明这个接口是一个函数式接口。
方法引用
要用::操作符分隔方法名与对象或类名。主要有三种情况:
- object::instanceMethod 对象::实例方法名
- Class::staticMethod 类名::静态方法名
- Class::instanceMethod 类名::实例方法名
前两种情况中,方法引用等价于提供方法参数的Lambda表达式。Math::pow等价于(x,y)->Math.pow(x,y)。
第三种情况,第1个参数会成为方法的目标。例如,String::compareToIngoreCose等价于(x,y)->x.compareToIngoreCase(y)。
可以在方法引用中使用this参数。例如,this::equals等同于x->this.equals(x)。
使用super也是合法的。super::instanceMethod 使用this做目标,会调用方法的超类版本。
构造器引用
和方法引用类似,方法名为new。采用哪一个构造器,取决于上下文。
可以用数组类型建立构造器引用。例如,int[]::new是一个构造器引用,它有一个参数:即数组的长度。这等价于Lambda表达式x->new int[x]。
变量工作域
lambda表达式有三个部分:
- 一个代码块
- 参数
- 自由变量的值
lambda表达式可以捕获外围作用域的中变量的值。在Java中,要确保捕获的值是明确定义的。在Lambda表达式中,只能引用值不会改变的的变量。
Lambda表达式中捕获的变量必须实际上是最终变量,实际上的最终变量是指,这个变量初始化之后就不会为它赋新值。
Lambda表达式中的体与嵌套块有相同的作用域。在lambda表达式中声明一个与局部变量同名的参数或局部变量是不合法的。
在一个Lambda表达式使用this关键字,是指创建这个Lambda表达式的方法的this参数。