1. 概述
匿名类类似于没有名称的嵌套类,而Lambda表达式是Java 8引入的,旨在推广函数式编程。在某些场景中,它们被用作匿名类的替代品。本文将探讨匿名类和Lambda表达式之间的区别。
2. 匿名内部类
匿名类实现接口和抽象类,但无需创建额外的子类。此外,匿名类没有名称,并且它在同一时间提供类定义并实例化。
现在,让我们看一个实现Runnable
接口的匿名类示例:
public class AnonymousClassExample{
public static void main(String[] args){
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
System.out.println("Thread: "+Thread.currentThread().getName()+" started");
}
});
t1.start();
}
}
在这个例子中,我们创建了一个名为AnonymousClassExample
的类,并使用匿名类实现了Runnable
接口。我们没有创建单独的Runnable
接口实现类。
3. Lambda表达式
Lambda表达式实现的是只有单个未实现方法的功能接口。Lambda表达式本质上是一个无名的方法定义,使得代码更简洁易读。它还提供了一种将函数作为方法参数传递的方式。
现在,让我们看看一个实现Runnable
接口的Lambda表达式示例:
public class LambdaExpressionExample{
public static void main(String[] args){
Thread t1 = new Thread(()->System.out.println("Thread: "+Thread.currentThread().getName()+" started"));
t1.start();
}
}
在这里,我们使用了一个匿名方法。这将解析为Runnable
接口的run()
方法。
即使在功能接口的情况下,我们也完全可以使用匿名类和Lambda表达式互换。但是它们之间存在差异。现在,让我们深入探讨这些差异。
4. 匿名类与Lambda表达式的比较
如前所述,匿名类用于接口和抽象类,而Lambda表达式仅用于功能接口。让我们来看看它们之间的更多差异。
4.1. 语法
匿名类同时提供类定义并实例化。我们使用new
关键字和正在实现的类或接口的名称。这就像调用构造函数,但我们还提供了方法实现并声明状态变量。匿名类是一个表达式,将其赋值给实现的类或接口的引用变量。因此,我们在末尾也添加分号。
另一方面,Lambda表达式是一个没有名称的方法。我们在功能接口的未实现方法签名中提供方法,无需提及方法参数的数据类型。方法在运行时解析。
4.2. this
关键字和变量的作用域
在匿名类中,this
关键字指的是匿名类本身。但在Lambda表达式中,this
指的是其包含类。
我们也可以在匿名类中声明成员变量,而在Lambda表达式中则不可行。因此,匿名类可以有状态。Lambda表达式内部声明的变量作为局部变量。两者都可以访问包含类的成员变量。
4.3. 编译过程
对于每个匿名类,编译时都会生成一个单独的类文件。类文件的格式是类名后跟美元符号和一个数字。例如,在名为TestClass
的类中定义匿名类时,编译后会创建一个额外的文件TestClass$1.class
。
另一方面,对于Lambda表达式,会在类文件中添加invokedynamic
指令。这个操作码指令有助于确定应调用的功能接口方法。
当编译Lambda表达式时,会在字节码中添加一个等效的私有静态或非静态方法。这个方法的签名与功能接口方法匹配。
此外,如果使用了Lambda表达式,捕获的参数也会在方法参数列表的开头。还会添加一个额外的indy
调用站点,其中包含了调用Lambda表达式生成的私有方法所需的所有信息。在运行时,调用站点被启动,并链接到的私有方法被调用。
4.4. 性能
现在让我们看看这两种方式对性能的影响。总体来说,Lambda表达式在性能上优于匿名类。这是因为匿名类会导致编译时额外生成一个类文件,这在类加载和运行时验证时会消耗更多时间。
Lambda表达式的性能更好,因为invokedynamic
指令在运行时动态绑定Lambda表达式到功能接口方法。Lambda表达式的第一次调用较慢,后续调用更快。
5. 总结
在这篇文章中,我们探讨了Lambda表达式和匿名内部类之间的差异。我们了解了它们在语法、编译过程和性能方面的不同。
如往常一样,示例代码可在GitHub上找到。