Java8的Lambdas与匿名类

由于Java8最近刚刚发布,它的全新lambdaexpression式看起来非常酷,所以我想知道这是否意味着我们习以为常的匿名类的消亡。

我一直在研究一些这方面的知识,并且发现了一些关于Lambdaexpression式如何系统地replace这些类的很酷的例子,比如Collection的Sort方法,它用来得到一个Comparator的Anonymous实例来执行sorting:

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只能用于单一的方法接口,这个新function只是一个仅在less数情况下使用的快捷方式,还是真的有用吗?

可以使用匿名内部类(AIC)来创build抽象类或具体类的子类。 AIC还可以提供一个接口的具体实现,包括添加状态(字段)。 AIC的一个实例可以在其方法体中使用,所以可以调用更多的方法,其状态可以随时间变化等。这些都不适用于lambdaexpression式。

我猜测AIC的大多数用途是提供单一函数的无状态实现,所以可以用lambdaexpression式replace,但是还有其他的用法,lambdaexpression式不能使用。 AICs在这里停留。

UPDATE

AIC和Lambdaexpression式的另一个区别是AIC引入了一个新的范围。 也就是说,名称是从AIC的超类和接口中parsing出来的,并且可以隐藏在词法环境中出现的名字。 对于lambdas,所有的名字都是词法parsing的。

Lambdas虽然是一个很棒的function,但只能用于SAMtypes。 也就是说,只有一个抽象方法的接口。 只要您的界面包含多个抽象方法,就会失败。 这就是匿名类将会有用的地方。

所以,不,我们不能忽略匿名的类。 只是FYI,通过跳过p1p2的types声明, sort()方法可以更简化:

 Collections.sort(personList, (p1, p2) -> p1.firstName.compareTo(p2.firstName)); 

您也可以在这里使用方法参考。 要么在Person类中添加一个compareByFirstName()方法,并使用:

 Collections.sort(personList, Person::compareByFirstName); 

或者,为firstName添加一个getter,直接从Comparator.comparing()方法获取Comparator

 Collections.sort(personList, Comparator.comparing(Person::getFirstName)); 

与匿名类的Lambda性能

当启动应用程序时,每个类文件都必须加载并validation。

编译器将匿名类作为给定类或接口的新子types进行处理,因此将为每个类生成一个新的类文件。

Lambdas在字节码生成方面有所不同,它们更高效,使用了JDK7附带的invokedynamic指令。

对于Lambdas来说,这个指令用来延迟在字节码运行时延迟翻译lambdaexpression式。 (指令只会在第一次被调用)

结果Lambdaexpression式将成为一个静态方法(在运行时创build)。 (与状态和状态有很小的区别,它们通过生成的方法参数来parsing)

Java 8中的Lambda被引入了函数式编程。 你可以在哪里避免样板代码。 我遇到了这个关于lambda的有趣的文章。

http://radar.oreilly.com/2014/04/whats-new-in-java-8-lambdas.html

对于简单的逻辑来说,build议使用lambda函数。 如果使用lambdas来实现复杂的逻辑将会在debugging代码的时候造成开销。

有以下不同之处:

1)语法

与匿名内部类(AIC)相比,Lambdaexpression式看起来很整洁

 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)范围

一个匿名的内部类是一个类,这意味着它具有在内部类中定义的variables的范围。

lambdaexpression式不是它自己的范围,而是包含范围的一部分。

在匿名内部类和lambdaexpression式中使用时,类似的规则适用于superthis关键字。 在匿名内部类的情况下,这个关键字指的是本地作用域,超级关键字指的是匿名类的超类。 而在lambdaexpression式的情况下,这个关键字引用封闭types的对象,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)性能

在运行时,匿名内部类需要类加载,内存分配和对象初始化,并调用非静态方法,而lambdaexpression式是纯编译时活动,在运行时不会产生额外的成本。 所以与匿名内部类相比,lambdaexpression式的性能更好。**

**我意识到这一点并不完全正确。 详情请参考以下问题。 Lambda与匿名内部类的性能:减lessClassLoader的负载?