Java8 Lambdas与匿名类

时间:2014-03-25 14:41:18

标签: java lambda java-8 anonymous-class

由于最近发布了Java8并且其全新的lambda表达式看起来非常酷,我想知道这是否意味着我们习以为常的匿名类的消亡。

我一直在研究这个问题,并找到了一些关于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只能用于单个方法接口,这个新功能只是一个仅在少数情况下使用的快捷方式,或者它是否真的有用吗?

9 个答案:

答案 0 :(得分:91)

匿名内部类(AIC)可用于创建抽象类或具体类的子类。 AIC还可以提供接口的具体实现,包括添加状态(字段)。可以在其方法体中使用this来引用AIC的实例,因此可以在其上调用其他方法,其状态可以随时间变化,等等。这些都不适用于lambdas。

我猜测AIC的大部分用途是提供单个函数的无状态实现,因此可以用lambda表达式替换,但是还有其他用途的AIC不能使用lambda。 AIC将继续留任。

<强>更新

AIC和lambda表达式之间的另一个区别是AIC引入了一个新的范围。也就是说,名称是从AIC的超类和接口中解析出来的,并且可以影响在词汇封闭环境中出现的名称。对于lambdas,所有名称都是词法解析的。

答案 1 :(得分:56)

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.comparing()方法获取Comparator

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

答案 2 :(得分:29)

使用匿名类的Lambda性能

启动应用程序时,必须加载并验证每个类文件。

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

Lambda与字节码生成不同,它们更高效,使用JDK7附带的invokedynamic指令。

对于Lambdas,此指令用于延迟在字节码中直接转换lambda表达式,直到运行时。 (仅在第一次调用指令)

结果Lambda表达式将成为静态方法(在运行时创建)。 (与stateles和statefull情况有一点不同,它们是通过生成的方法参数解决的)

答案 3 :(得分:10)

存在以下差异:

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表达式中使用内容时,类似的规则适用于超级关键字。在匿名内部类的情况下,此关键字引用本地范围,而超级关键字引用匿名类的超类。在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?

答案 4 :(得分:3)

引入了Java 8中的Lambda用于函数式编程。在哪里可以避免样板代码。我在lambda上发现了这篇有趣的文章。

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

建议将lambda函数用于简单逻辑。如果在发生问题时使用lambdas实现复杂逻辑将是调试代码的开销。

答案 5 :(得分:1)

让我们比较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 –包含一个匿名类 enter image description here

Lambda表达式:使用Lambda表达式,编译器将仅创建1个类文件,如下所示。 Lambda Expression

因此Java将为每个使用的匿名类创建新的类文件。

4。效果

匿名类由编译器作为给定类或接口的新子类型处理,因此将为使用的每个匿名类生成一个新的类文件。启动应用程序时,将加载并验证为匿名类创建的每个类。当您有大量的匿名类时,此过程非常耗时。

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

答案 6 :(得分:1)

+----------------------------------+----------------------------------------------------------+---------------------------------------------+
|                                  |                                       Lambdas            |              Anonymous classes              |
+----------------------------------+----------------------------------------------------------+---------------------------------------------+
| Definition                       | An anonymous method that can be created without belonging| An inner class without a name.              |
|                                  | to any class                                             |                                             |
+----------------------------------+----------------------------------------------------------+---------------------------------------------+
| Scope of variables of main class | Available                                                | Not available                               |
| (this and super keywords also)   |                                                          |                                             |
+----------------------------------+----------------------------------------------------------+---------------------------------------------+
| Lines of codes                   | Reduced the lines of code. It’s a short form of          | Have more lines of code compared to lambdas |
|                                  | anonymous class.                                         |                                             |
+----------------------------------+----------------------------------------------------------+---------------------------------------------+
| Criteria for creating            | Needs to be Functional Interface, ie interface with      | Can use interfaces(including Functional     |
|                                  | only one abstract method. Example : Runnable Interface   | interfaces) and abstract classes to create. |
+----------------------------------+----------------------------------------------------------+---------------------------------------------+
| Example:                         | Runnable r = ()->{System.out.println("Hello World");};   | Runnable r = new Runnable() {               |
|                                  |                                                          |         @Override                           |
|                                  |                                                          |         public void run() {                 |
|                                  |                                                          |          System.out.println("Hello World"); |
|                                  |                                                          |                                        }    |
|                                  |                                                          |     };                                      |
+----------------------------------+----------------------------------------------------------+---------------------------------------------+

答案 7 :(得分:0)

  • lambda语法不需要编写Java可以推断出的显而易见的代码。
  • 通过使用invoke dynamic lambda不会在编译期间转换回匿名类(Java不必经历创建对象的过程,只需关心方法的签名,就可以绑定到方法而不创建对象
  • lambda更加强调我们想做的事情,而不是我们必须要做的事

答案 8 :(得分:0)

匿名类仍然存在,因为lambda对于具有单个抽象方法的函数很有用,但在所有其他情况下,匿名内部类是您的救星。