首页 文章

Java8 Lambdas与匿名类

提问于
浏览
86

由于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?

EDIT

同样的问题,但在相反的方向,使用Lambdas而不是匿名类有什么好处,因为Lambdas只能用于单个方法接口,这个新功能只是在少数情况下使用的快捷方式还是真的有用?

5 回答

  • 84

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

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

    UPDATE

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

  • 52

    Lambda虽然是一个很棒的功能,但只适用于SAM类型 . 也就是说,只有一个抽象方法的接口 . 只要您的界面包含多个抽象方法,它就会失败 . 这就是匿名类有用的地方 .

    所以,不,我们不能只忽略匿名类 . 只是FYI,通过跳过 p1p2 的类型声明,可以更简化您的 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));
    
  • 8

    Lambda performance with Anonymous classes

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

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

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

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

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

  • 4

    有以下差异:

    1) Syntax

    与匿名内部类(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)Scope

    匿名内部类是一个类,这意味着它具有在内部类中定义的变量的作用域 .

    鉴于 lambda expression is not a scope of its own, but is part of the enclosing scope.

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

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

    **我确实意识到这一点并不完全正确 . 有关详情,请参阅以下问题 . Lambda vs anonymous inner class performance: reducing the load on the ClassLoader?

  • 28

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

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

    建议使用lambda函数进行简单逻辑 . 如果使用lambdas实现复杂逻辑,则在发生问题时调试代码会产生开销 .

相关问题