由于Java8最近已经发布,并且它的全新lambda表达式看起来非常酷,我想知道这是否意味着我们习以为常的Anonymous类的消亡。
我一直在研究这个问题,并找到了一些关于Lambda表达式如何系统地替换这些类的很酷的例子,例如Collection的sort方法,它用于获取Comparator的Anonymous实例来执行排序:
Collections.sort(personList, new Comparator<Person>(){
public int compare(Person p1, Person p2){
return p1.firstName.compareTo(p2.firstName);
}
});
现在可以使用Lambdas完成:
Collections.sort(personList, (Person p1, Person p2) -> p1.firstName.compareTo(p2.firstName));
而且看起来非常简洁。所以我的问题是,有没有理由继续在Java8中使用这些类而不是Lambdas?
编辑
同样的问题,但在相反的方向,使用Lambdas而不是匿名类有什么好处,因为Lambdas只能用于单个方法接口,这个新功能只是在少数情况下使用的快捷方式还是真的有用?
匿名内部类(AIC)可用于创建抽象类或具体类的子类。 AIC还可以提供接口的具体实现,包括添加状态(字段)。可以在其方法体中使用this
来引用AIC的实例,因此可以在其上调用其他方法,其状态可以随时间变化,等等。这些都不适用于lambdas。
我猜测AIC的大部分用途是提供单个函数的无状态实现,因此可以用lambda表达式替换,但是还有其他用途的AIC不能使用lambda。 AIC将留在这里。
UPDATE
AIC和lambda表达式之间的另一个区别是AIC引入了一个新的范围。也就是说,名称是从AIC的超类和接口中解析出来的,并且可以影响在lexicallyenclosing环境中出现的名称。对于lambdas,所有名称都是词法解析的。
Lambda虽然是一个很棒的功能,但只适用于SAM类型。也就是说,只有一个抽象方法的接口。只要您的界面包含多个抽象方法,它就会失败。这就是匿名类有用的地方。
所以,不,我们不能只忽略匿名类。只是仅供参考,通过跳过sort()
和p1
的类型声明,您的p2
方法可以更简化:
Collections.sort(personList, (p1, p2) -> p1.firstName.compareTo(p2.firstName));
您也可以在此处使用方法参考。要么在compareByFirstName()
类中添加Person
方法,请使用:
Collections.sort(personList, Person::compareByFirstName);
或者,为firstName
添加一个getter,直接从Comparator
方法获取Comparator.comparing()
:
Collections.sort(personList, Comparator.comparing(Person::getFirstName));
使用匿名类进行Lambda性能
启动应用程序时,必须加载并验证每个类文件。
编译器将匿名类作为给定类或接口的新子类进行处理,因此将为每个类生成一个新的类文件。
Lambda与字节码生成不同,它们更有效,使用JDK7附带的invokedynamic指令。
对于Lambdas,此指令用于延迟在字节码中直接转换lambda表达式,直到运行时。 (仅在第一次调用指令)
因此,Lambda表达式将成为静态方法(在运行时创建)。 (与stateles和statefull情况有一点不同,它们是通过生成的方法参数解决的)
有以下差异:
1)语法
与匿名内部类(AIC)相比,Lambda表达式看起来很整洁
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("in run");
}
};
Thread t = new Thread(r);
t.start();
}
//syntax of lambda expression
public static void main(String[] args) {
Runnable r = ()->{System.out.println("in run");};
Thread t = new Thread(r);
t.start();
}
2)范围
匿名内部类是一个类,这意味着它具有在内部类中定义的变量的作用域。
然而,lambda表达式不是它自己的范围,而是封闭范围的一部分。
当在匿名内部类和lambda表达式中使用时,类似的规则适用于super和this关键字。在匿名内部类的情况下,此关键字引用本地范围,而超级关键字引用匿名类的超类。在lambda表达式的情况下,此关键字引用封闭类型的对象,而super将引用封闭类的超类。
//AIC
public static void main(String[] args) {
final int cnt = 0;
Runnable r = new Runnable() {
@Override
public void run() {
int cnt = 5;
System.out.println("in run" + cnt);
}
};
Thread t = new Thread(r);
t.start();
}
//Lambda
public static void main(String[] args) {
final int cnt = 0;
Runnable r = ()->{
int cnt = 5; //compilation error
System.out.println("in run"+cnt);};
Thread t = new Thread(r);
t.start();
}
3)表现
在运行时,匿名内部类需要类加载,内存分配和对象初始化以及非静态方法的调用,而lambda表达式是纯编译时活动,并且在运行时不会产生额外的成本。因此,与匿名内部类相比,lambda表达式的性能更好。**
**我确实意识到这一点并不完全正确。有关详情,请参阅以下问题。 Lambda vs anonymous inner class performance: reducing the load on the ClassLoader?
Java 8中的Lambda是为函数式编程而引入的。在哪里可以避免样板代码。我遇到了关于lambda的这篇有趣的文章。
http://radar.oreilly.com/2014/04/whats-new-in-java-8-lambdas.html
建议使用lambda函数进行简单逻辑。如果使用lambdas实现复杂逻辑,则在发生问题时调试代码会产生开销。
让我们比较Lambda Expression和Anonymous类之间的差异。
1.语法
匿名课程:
package com.onlyfullstack;
public class LambdaVsAnonymousClass {
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("Anonymous class");
}
};
Thread thread = new Thread(runnable);
thread.start();
}
}
LAMBDA:
package com.onlyfullstack;
public class LambdaVsAnonymousClass {
public static void main(String[] args) {
Runnable runnable = () -> System.out.println("Lambda Expression");
Thread thread = new Thread(runnable);
thread.start();
}
}
2.实施
匿名类可用于实现具有任意数量抽象方法的任何接口。 Lambda Expression仅适用于SAM(单一抽象方法)类型。这只是一个抽象方法的接口,也称为功能接口。只要您的界面包含多个抽象方法,它就会失败。
3.编译
匿名类:Java为LambdaVsAnonymousClass java文件创建两个类文件,作为LambdaVsAnonymousClass.class - 包含主程序LambdaVsAnonymousClass $ 1.class - 包含一个匿名类
Lambda表达式:使用Lambda表达式编译器将只创建1个类文件,如下所示。
因此,Java将为每个使用的Anonymous类创建新的类文件。
4.表现
编译器将匿名类作为给定类或接口的新子类进行处理,因此将为每个使用的匿名类生成一个新的类文件。启动应用程序时,将加载并验证为Anonymous类创建的每个类。当您拥有大量匿名类时,此过程非常耗时。
Lambda表达式而不是为lambda生成直接字节码(如提议的匿名类语法糖方法),编译器声明一个配方(通过invokeDynamic指令)并将实际构造方法委托给运行时。
因此Lambda表达式比Anonymous类更快,因为它们仅在被调用时执行。
有关更多信息,请参阅以下链接:
https://onlyfullstack.blogspot.com/2019/02/lambda-vs-anonymous-class-in-java-8.html
https://onlyfullstack.blogspot.com/2019/02/how-lambda-internally-works-in-java-8.html
invoke dynamic
,lambda在编译期间不会转换回匿名类(Java不必经历创建对象,只关心方法的签名,可以绑定到方法而不创建对象匿名类是留下来的,因为lambda适用于具有单个抽象方法的函数,但对于所有其他情况,匿名内部类是您的救星。