原文:https://zhuanlan.zhihu.com/p/90815478
List list = function.apply(10);
使用 Lambda 表达式,我们一般可以这样写:
Function<Integer, ArrayList> function = n -> new ArrayList(n);
使用「引用构造方法」的方式,我们可以简写成这样:
Function<Integer, ArrayList> function = ArrayList::new;
自定义函数接口很容易,只需要编写一个只有一个抽象方法的接口即可,示例代码:
@FunctionalInterface public interface MyInterface<T> { void function(T t); }
上面代码中的 @FunctionalInterface
是可选的,但加上该注解编译器会帮你检查接口是否符合函数接口规范。就像加入 @Override
注解会检查是否重写了函数一样。
经过上面的介绍,我们看到 Lambda 表达式只是为了简化匿名内部类书写,看起来似乎在编译阶段把所有的 Lambda 表达式替换成匿名内部类就可以了。但实际情况并非如此,在 JVM 层面,Lambda 表达式和匿名内部类其实有着明显的差别。
匿名内部类仍然是一个类,只是不需要我们显式指定类名,编译器会自动为该类取名。比如有如下形式的代码:
public class LambdaTest { public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { System.out.println("Hello World"); } }).start(); } }
编译之后将会产生两个 class 文件:
LambdaTest.class LambdaTest$1.class
使用 javap -c LambdaTest.class
进一步分析 LambdaTest.class
的字节码,部分结果如下:
public static void main(java.lang.String[]); Code: 0: new #2 // class java/lang/Thread 3: dup 4: new #3 // class com/example/myapplication/lambda/LambdaTest$1 7: dup 8: invokespecial #4 // Method com/example/myapplication/lambda/LambdaTest$1."<init>":()V 11: invokespecial #5 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V 14: invokevirtual #6 // Method java/lang/Thread.start:()V 17: return
可以发现在 4: new #3
这一行创建了匿名内部类的对象。
接下来我们将上面的示例代码使用 Lambda 表达式实现,代码如下:
public class LambdaTest { public static void main(String[] args) { new Thread(() -> System.out.println("Hello World")).start(); } }
此时编译后只会产生一个文件 LambdaTest.class
,再来看看通过 javap 对该文件反编译后的结果:
public static void main(java.lang.String[]); Code: 0: new #2 // class java/lang/Thread 3: dup 4: invokedynamic #3, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable; 9: invokespecial #4 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V 12: invokevirtual #5 // Method java/lang/Thread.start:()V 15: return
从上面的结果我们发现 Lambda 表达式被封装成了主类的一个私有方法,并通过 invokedynamic
指令进行调用。
因此,我们可以得出结论:Lambda 表达式是通过 invokedynamic
指令实现的,并且书写 Lambda 表达式不会产生新的类。
既然 Lambda 表达式不会创建匿名内部类,那么在 Lambda 表达式中使用 this
关键字时,其指向的是外部类的引用。
优点:
缺点: