Lambda 表达式描述了一个代码块(或者叫匿名方法),可以将其作为参数传递给构造方法或者普通方法以便后续执行。考虑下面这段代码:
() -> System.out.println("Lambda")
() 为 Lambda 表达式的参数列表(本例中没有参数),-> 标识这串代码为 Lambda 表达式,System.out.println("Lambda")
为要执行的代码,即将“输出内容”打印到标准输出流。
Runnable 接口定义如下:
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
创建一个线程并启动:
public class ThreadTest {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Lambda");
}
}).start();
}
}
通过 Lambda 表达式呢?只需要下面这样:
public class LamadaTest {
public static void main(String[] args) {
new Thread(() -> System.out.println("Lambda")).start();
}
}
比起匿名内部类,Lambda 表达式不仅易于理解,更大大简化了必须编写的代码数量。
Lambda 语法
每个 Lambda 表达式都遵循以下法则:
(parameter-list) -> {expression-or-statements}
() 中的 parameter-list 是以逗号分隔的参数。你可以指定参数的类型,也可以不指定(编译器会根据上下文进行推断)。Java 11 后,还可以使用 var 关键字作为参数类型。
{} 中的 expression-or-statements 为 Lambda 的主体,可以是一行语句,也可以多行。
可以通过 Lambda 表达式干很多事情,比如说
- 为变量赋值:
Runnable r = () -> { System.out.println("Lambda"); };
r.run();
- 作为 return 结果:
static FileFilter getFilter(String ext) {
return (pathname) -> pathname.toString().endsWith(ext);
}
- 作为数组元素:
final PathMatcher matchers[] = {
(path) -> path.toString().endsWith("txt"),
(path) -> path.toString().endsWith("java")
};
- 作为普通方法或者构造方法的参数:
Thread thread = new Thread(() -> System.out.println("Lambda")).start();
需要注意 Lambda 表达式的作用域范围。
public static void main(String[] args) {
int limit = 10;
Runnable r = () -> {
int limit = 5;
for (int i = 0; i < limit; i++)
System.out.println(i);
};
}
上面这段代码在编译的时候会提示错误:变量 limit 已经定义过了。
和匿名内部类一样,不要在 Lambda 表达式主体内对方法内的局部变量进行修改,否则编译也不会通过:Lambda 表达式中使用的变量必须是 final 的。
这个问题发生的原因是因为 Java 规范中是这样规定的:
Lambda 表达式中要用到的,但又未在 Lambda 表达式中声明的变量,必须声明为 final 或者是 effectively final,否则就会出现编译错误。
关于 final 和 effectively final 的区别:
final int a; a = 1; // a = 2; // 由于 a 是 final 的,所以不能被重新赋值 int b; b = 1; // b 此后再未更改 // b 就是 effectively final int c; c = 1; // c 先被赋值为 1,随后又被重新赋值为 2 c = 2; // c 就不是 effectively final
如果把 limit 定义为 final,那就无法在 Lambda 表达式中修改变量的值。那有什么好的解决办法呢?既能让编译器不发出警告,又能修改变量的值。
有 3 个可行的解决方案:
1)把 limit 变量声明为 static。
2)把 limit 变量声明为 AtomicInteger。
3)使用数组。
Lambda 与 this 关键字
Lambda 表达式并不会引入新的作用域,这一点和匿名内部类是不同的。也就是说,Lambda 表达式主体内使用的 this 关键字和其所在的类实例相同。
参考:
https://blog.youkuaiyun.com/qing_gee/article/details/104231239