在Java 8中,为了能够将行为参数化而引入了Lambda表达式。
可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。
语法
Lambda表达式在Java语言中引入了->
操作符,->
操作符被称为Lambda表达式的操作符或者箭头操作符,它将Lambda表达式分为两部分:
-
左侧部分指定了Lambda表达式需要的所有参数,Lambda表达式本质上是对接口的实现,Lambda表达式的参数列表本质上对应着接口中方法的参数列表。
-
右侧部分指定了Lambda体,即Lambda表达式要执行的功能,Lambda体本质上就是接口方法具体实现的功能。
Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器能够通过上下文推断出数据类型,这就是类型推断
。
- 左侧部分只有一个参数时,小括号可以省略
- Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器能够通过上下文推断出数据类型,这就是“类型推断”
public class LambdaTest {
public void completedLeft() {
// 类型String可以省略,由上下文进行类型推断;因为只有一个参数,小括号可以省略
Consumer<String> consumer = (String e) -> println(e.substring(3, 6));
}
public void singleLeft() {
Consumer<String> consumer = e -> println(e.substring(3, 6));
}
}
- 右侧部分只有一行语句时,大括号和
return
关键字可以省略
public class LambdaTest {
public String completeRight() {
Supplier<String> supplier = () -> {
String result = new Random().nextInt(100) + "";
return result.substring(1);
};
return supplier.get();
}
public String simpleRight() {
Supplier<String> supplier = () -> new Random().nextInt(100) + "";
return supplier.get();
}
}
- 无参无返回值
public interface INoArgsAndNoReturn {
void execute();
}
public class LambdaTest {
public void noArgsAndNoReturn() {
INoArgsAndNoReturn lambda = () -> println("execute lambda");
execute(lambda);
}
private void execute(INoArgsAndNoReturn lambda) {
lambda.execute();
}
}
- 无参有返回值
public interface INoArgsAndHasReturn {
String execute();
}
public class LambdaTest {
public void noArgsAndHasReturn() {
INoArgsAndHasReturn lambda = () -> "return lambda result";
execute(lambda);
}
private void execute(INoArgsAndHasReturn lambda) {
println(lambda.execute());
}
}
- 有参无返回值
public interface IHasArgsAndNoReturn {
void execute(Integer one, Integer two);
}
public class LambdaTest {
public void hasArgsAndNoReturn() {
IHasArgsAndNoReturn lambda = (a, b) -> println(a + b);
execute(lambda);
}
private void execute(IHasArgsAndNoReturn lambda) {
lambda.execute(1, 2);
}
}
- 有参有返回值
public interface IHasArgsAndHasReturn {
String execute(Integer one, Integer two);
}
public class LambdaTest {
public void hasArgsAndHasReturn() {
execute((x, y) -> x + " " + y);
}
private void execute(IHasArgsAndHasReturn argsReturn) {
println(argsReturn.execute(1, 2));
}
}
使用局部变量
Lambda可以没有限制地捕获(也就是在其主体中引用)实例变量和静态变量。但局部变量必须显式声明为
final,或事实上是 final 。
对局部变量的限制
第一,实例变量和局部变量背后的实现有一个关键不同。实例变量都存储在堆中,而局部变量则保存在栈上。
如果Lambda可以直接访问局部变量,而且Lambda是在一个线程中使用的,则使用Lambda的线程,可能会在分
配该变量的线程将这个变量收回之后,去访问该变量。因此,Java在访问*局部变量时,实际上是在访问它
的副本,而不是访问原始变量。如果局部变量仅仅赋值一次那就没有什么区别了——因此就有了这个限制。
第二,这一限制不鼓励你使用改变外部变量的典型命令式编程模式,这种模式会阻碍很容易做到的并行处理。
经验总结
- lambda表达式通过传递代码,来使代码根据条件被延迟执行或不执行。
- 多参数lambda通过创建匹配的函数式接口来实现。