介绍
lambda 表达式本质上是一个匿名方法,用于实现函数式接口(functional interface)定义的方法。这就产生了匿名类。lambda 也称为闭包(closures)。
函数式接口是仅有一个抽象方法的接口。如 Runnable 接口仅有 run()
方法,该方法表明了接口的目的。函数式接口定义了 lambda 表达式的目标类型,lambda 表达式仅可用于特定的目标类型的上下文中。函数式接口又称 SAM 类型,SAM 指 Single Abstract Method。
Object 类中的公有方法隐式是函数式接口的成员,因为函数式接口的实例自动实现了这些方法。
->
称为 lambda 操作符或箭头操作符,它将 lambda 表达式划分成了两个部分。左边是参数,参数可为空;右边是 lambda 体,表示 lambda 表达式的行为。lambda 体可以是单个表达式,也可以是块。
lambda 表达式出现在目标类型上下文中时,自动创建实现了函数式接口的类实例,lambda 表达式作为函数式接口中抽象方法的实现。lambda 表达式的参数类型和返回类型、参数个数必须和函数式接口中抽象方法定义的兼容、对应。
interface A {
double get();
}
// lambda 表达式作为 get() 方法的实现
A a = () -> 1.2;
// 返回 1.2
a.get();
lambda 表达式中的参数可以指定类型,也可以不指定根据上下文自行推断。
函数式接口类型变量可以执行和它兼容的任意 lambda 表达式。
当 lambda 表达式仅有一个参数时,加不加小括号都可以。n -> n * 2
等价于 (n) -> n * 2
。
对于多参数 lambda 表达式,如果想为参数声明类型,则必须为所有参数声明类型。
块 lambda 表达式
->
右边为块的 lambda 表达式称为块 lambda 表达式。使用块 lambda 表达式必须使用 return 语句。lambda 块本质上类似于方法体。
泛型函数接口
函数式接口可以是泛型接口,此时关联的 lambda 表达式的类型由泛型接口中的类型指定。
interface A<T> {}
// 类型为 Integer
A<Integer> a = (...) -> {...};
// 类型为 String
A<String> a = (...) -> {...};
lambda 表达式作参数
能接收 lambda 表达式的参数,类型必须是可以和该 lambda 表达式兼容的函数式接口。
lambda 表达式和异常
lambda 表达式中抛出异常时,函数式接口中抽象方法的 throws 子句必须有对应的异常。
lambda 表达式和变量捕获
在 lambda 表达式中,可以访问 lambda 表达式自身所在的作用域中定义的静态变量、类实例、表示该作用域的 this 等。在 lambda 表达式中访问该作用域中的局部变量的过程称为变量捕获(variable capture)。能够访问的局部变量要么是 effectively final 变量,要么是 final 修饰的变量。effectively final 变量是赋过初值之后值没有变化的变量。在 lambda 表达式中不能修改局部变量的值,在 lambda 表达式自身所在的作用域内也不能修改,因为这样做变量将不再是 effectively final。
方法引用
方法引用可以引用一个方法但不执行它。方法引用和 lambda 表达式有关,因为它也需要一个兼容函数式接口的目标类型上下文。方法引用可以创建函数式接口的实例。
静态方法的方法引用一般形式为:ClassName::methodName
,可以在目标类型兼容的任何地方使用。
实例方法的方法引用一般形式为:objRef::methodName
。
如果想指定一个给定类的任意实例都可以使用的实例方法,而不是某个特定的类,可以这样表示方法引用:ClassName::instanceMethodName
,这个方法引用对应的函数式接口中的方法需要两个参数,第一个参数匹配调用对象本身,即 ClassName
实例;第二个参数匹配 instanceMethodName
需要的参数。
interface MyFunc<T> {
boolean func(T v1, T v2);
}
class High {
private int h;
public High(int h) {
this.h = h;
}
boolean same(High high) {
return h == high.h;
}
}
public class InstanceMethod {
static <T> int counter(T[] vals, MyFunc<T> f, T v) {
int count = 0;
for (int i = 0; i < vals.length; i++) {
if (f.func(vals[i], v)) {
count++;
}
}
return count;
}
public static void main(String[] args) {
int count;
High[] week = {new High(10), new High(20), new High(31)};
// same 与 MyFunc 接口兼容,调用 same 方法的对象作为
// func 函数的第一个参数,传入 same 方法的对象作为 func
// 函数的第二个参数
count = counter(week, High::same, new High(10));
System.out.println(count);
}
}
泛型类、泛型方法也可以作为方法引用。泛型方法的方法引用形式为:ClassName::<Type>method
,泛型类的方法引用形式为:Class<Type>::method
。
构造器引用
构造器引用一般形式为:Class::new
,可以赋值给与构造器兼容的函数式接口类型的变量。
泛型类、泛型方法的使用与前述类似,只不过方法名是 new
。
数组的构造器引用形式为:type[]::new
。
预定义的函数接口
接口 | 描述 |
---|---|
UnaryOperator<T> |
对 T 类型对象使用一元操作符,返回 T 类型对象,方法为 apply() |
BinaryOperator<T> |
对两个 T 类型对象使用二元操作符,返回 T 类型对象,方法为 apply() |
Consumer<T> |
对 T 类型对象进行操作,方法为 accept() |
Supplier<T> |
返回 T 类型对象,方法为 get() |
Function<T, R> |
对 T 类型对象进行操作,返回 R 类型对象,方法为 apply() |
Predicate<T> |
判断 T 类型对象是否满足某个条件,据此返回 boolean 值,方法为 test() |
参考
[1] Herbert Schildt, Java The Complete Reference 11th, 2019.