1 初识 Lambda 表达式
Lambda 表达式是 Java 8 引入的一个重要特性,它描述了一个代码块(或称为匿名方法),可以将其作为参数传递给构造方法或普通方法以便后续执行。Lambda 表达式的基本语法如下:
java
() -> System.out.println("沉默王二")
()
表示 Lambda 表达式的参数列表(本例中没有参数)。->
是 Lambda 表达式的标识符,表示这是一个 Lambda 表达式。System.out.println("沉默王二")
是要执行的代码,即将"沉默王二"打印到标准输出流。
2 Lambda 表达式与 Runnable
接口
在 Java 中,Runnable
接口是多线程编程的基础接口,其定义如下:
java
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
Runnable
接口只有一个抽象方法 run()
,并且使用了 @FunctionalInterface
注解。这个注解表示该接口是一个函数式接口,可以通过 Lambda 表达式创建其实例。
在 Java 8 之前,创建一个线程并启动它通常需要使用匿名内部类:
java
public class LamadaTest {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("沉默王二");
}
}).start();
}
}
而使用 Lambda 表达式,代码可以大大简化:
java
public class LamadaTest {
public static void main(String[] args) {
new Thread(() -> System.out.println("沉默王二")).start();
}
}
Lambda 表达式不仅易于理解,还大大简化了代码量。
3 Lambda 表达式的语法
每个 Lambda 表达式都遵循以下语法规则:
java
( parameter-list ) -> { expression-or-statements }
()
中的parameter-list
是以逗号分隔的参数。你可以指定参数的类型,也可以不指定(编译器会根据上下文进行推断)。->
是 Lambda 表达式的标识符。{}
中的expression-or-statements
为 Lambda 的主体,可以是一行语句,也可以是多行语句。
Lambda 表达式可以用于多种场景,例如:
-
为变量赋值:
javaRunnable r = () -> { System.out.println("沉默王二"); }; r.run();
-
作为返回结果:
javastatic FileFilter getFilter(String ext) { return (pathname) -> pathname.toString().endsWith(ext); }
-
作为数组元素:
javafinal PathMatcher matchers[] = { (path) -> path.toString().endsWith("txt"), (path) -> path.toString().endsWith("java") };
-
作为普通方法或构造方法的参数:
javanew Thread(() -> System.out.println("沉默王二")).start();
4 Lambda 表达式的作用域
Lambda 表达式并不会引入新的作用域,因此在 Lambda 表达式中使用的变量必须是 final
或 effectively final
。例如:
java
public static void main(String[] args) {
int limit = 10;
Runnable r = () -> {
int limit = 5; // 编译错误:变量 limit 已经定义过了
for (int i = 0; i < limit; i++)
System.out.println(i);
};
}
上述代码会导致编译错误,因为 limit
变量在 Lambda 表达式中被重新定义。
5 解决 Lambda 表达式中的变量修改问题
如果需要在 Lambda 表达式中修改局部变量的值,可以考虑以下几种解决方案:
-
将变量声明为
static
:javapublic class ModifyVariable2StaticInsideLambda { static int limit = 10; public static void main(String[] args) { Runnable r = () -> { limit = 5; for (int i = 0; i < limit; i++) { System.out.println(i); } }; new Thread(r).start(); } }
-
使用
AtomicInteger
:javapublic class ModifyVariable2AtomicInsideLambda { public static void main(String[] args) { final AtomicInteger limit = new AtomicInteger(10); Runnable r = () -> { limit.set(5); for (int i = 0; i < limit.get(); i++) { System.out.println(i); } }; new Thread(r).start(); } }
-
使用数组:
javapublic class ModifyVariable2ArrayInsideLambda { public static void main(String[] args) { final int[] limits = {10}; Runnable r = () -> { limits[0] = 5; for (int i = 0; i < limits[0]; i++) { System.out.println(i); } }; new Thread(r).start(); } }
6 Lambda 表达式与 this
关键字
Lambda 表达式并不会引入新的作用域,因此在 Lambda 表达式中使用的 this
关键字与外部类的 this
关键字相同。例如:
java
public class LamadaTest {
public static void main(String[] args) {
new LamadaTest().work();
}
public void work() {
System.out.printf("this = %s%n", this);
Runnable r = new Runnable() {
@Override
public void run() {
System.out.printf("this = %s%n", this);
}
};
new Thread(r).start();
new Thread(() -> System.out.printf("this = %s%n", this)).start();
}
}
Tips:%s
代表当前位置输出字符串,%n
代表换行符,也可以使用 \n
代替,但 %n
是跨平台的。
分析 :
work()
方法中的代码可以分为 3 个部分:
- 单独的
this
关键字
java
System.out.printf("this = %s%n", this);
其中 this
为 main()
方法中通过 new
关键字创建的 LamadaTest
对象------new LamadaTest()
。
- 匿名内部类中的 this 关键字
java
Runnable r = new Runnable()
{
@Override
public void run()
{
System.out.printf("this = %s%n", this);
}
};
其中 this
为 work()
方法中通过 new 关键字创建的 Runnable
对象------new Runnable(){...}
。
- Lambda 表达式中的
this
关键字
其中this
关键字和 1)中的相同。
输出结果如下:
java
this = com.cmower.java_demo.journal.LamadaTest@3feba861
this = com.cmower.java_demo.journal.LamadaTest$1@64f033cb
this = com.cmower.java_demo.journal.LamadaTest@3feba861
7 总结
Lambda 表达式极大地简化了 Java 编程,特别是在处理函数式接口时。然而,不当的使用可能会导致不必要的混乱,因此在使用 Lambda 表达式时需要谨慎。理解 Lambda 表达式的语法、作用域以及与 this
关键字的关系,将有助于更好地利用这一强大的特性。