【问题标题】:Is Java Lambda expression is similar logic of Groovy closure?Java Lambda 表达式是否类似于 Groovy 闭包的逻辑?
【发布时间】:2014-10-23 06:33:30
【问题描述】:

我正在学习 Java 8 的新功能 Lambda 表达式。这是我使用 Lambda 表达式的“HelloWorld”类

public class LambdaHelloWorld {
    interface HelloWorld {
        String sayHello(String name);
    }

    public static void main(String[] args) {          
         HelloWorld helloWorld = (String name) -> { return "Hello " + name; };
         System.out.println(helloWorld.sayHello("John Doe"));
    }
}

这种风格与 Groovy 闭包非常相似。这是 groovy “HelloWorld”

def sayHello(name) {
        println("Hello $name!")
}

def clos = {name -> sayHello(name)} 
clos.call('John Doe')

我认为这两个代码彼此之间的差异较小。Java Lambda 表达式是类似于 Groovy 闭包的逻辑或风格吗?

【问题讨论】:

  • 您可以简单地写成name -> "Hello "+name 而不是(String name) -> { return "Hello " + name; },这要短得多。顺便说一句,在对 Groovy 了解不多的情况下,这些示例看起来并不等同于我。 Java 示例是一个将String 值映射到String 值的函数,而您的Groovy 示例看起来像一个使用String 的操作。
  • @Holger 或者更短的 "Hello "::concat

标签: java groovy lambda java-8


【解决方案1】:

在 Java 8(使用 lambda)或 Groovy(使用闭包)中实现所谓的函数式接口看起来非常相似,但底层机制却大不相同。我们以java.util.function.Consumer功能接口为例。我们使用它在名为 myList 的假设 java.util.List 实例上调用新的 Java 8 forEach() 方法。

在 Java 中是这样的:

myList.forEach ((s) -> System.out.println(s));

在 Groovy 中也是如此:

myList.forEach { s -> println s }

两个编译器都从 lambda / 闭包代码生成新的类。 Java 8 生成的类实现了目标接口(在这种情况下为Consumer),不是从任何东西派生的,类似于这样的嵌入式匿名类:

myList.forEach(new Consumer<Object>() {
    @Override
    public void accept (Object s) {
        System.out.println(s);
    }
});

相比之下,Groovy 生成的内容有点像下面这样:

myList.forEach (new Closure(this) {
    void doCall(Object s) {
        println s
    }
}

这会创建一个派生自groovy.lang.Closure 的匿名类,它 实现任何特定接口。不过,它可以在这里用作参数。这是可能的,因为 Groovy 在运行时生成一个动态代理对象,实现“Consumer”接口并将任何调用转发到生成的闭包实例。

因此,您可以用 Groovy 闭包替换 Java 8 lambda,但反之则不行。当您想在 Java 8 代码中使用 Groovy API 时,您不能调用需要带有 lambda 表达式的闭包的方法。 Closure 不是函数式接口,而是一个抽象类,它根本无法通过 lambda 表达式实现。

【讨论】:

  • 在 Java 中,您还可以省略单个 lambda 参数的圆括号:s -&gt; System.out.println(s),因此它看起来更像在 Groovy 中。此外,Java 编译器 创建Consumer 实现它只创建一个方法 保存lambda 的代码。调用该方法的Consumer 实现是在运行时生成的。这是 Java lambda 相对于其他方法(如旧的内部类)的一大优势,您不会有大量的 .class 文件到处乱飞。如果您使用方法引用,System.out::println,则甚至不会生成方法。
  • @Holger 在 C# 中你可以写成myList.forEach(System.out.println)。在Java中可能吗?不是 Java 开发人员,只是问问。
  • @nawfal:Java 语法是myList.forEach(System.out::println)。这是因为System.out 是一个表达式,其计算结果为PrintStream 实例,而::println 是一个方法引用,指向PrintStream 中名为println 的实例方法。
  • @nawfal:甚至可以使用参数,例如Foo::new 而不是 (x,y)-&gt;new Fo(x,y) 和数组,所以流 API 允许类似:Foo[] array =myList.stream().toArray(Foo[]::new);。更花哨的是,在实例方法中,您可以将 super-invocation 绑定到函数,例如list.forEach(super::doSometing); 为每个元素调用一个超类方法……
  • @Holger 哇,看起来很酷。关于超级调用,在 C# 中很容易实现,例如 list.forEach(doSometing)list.forEach(base.doSometing)
【解决方案2】:

Java 的 lambda 也是闭包。这些在抽象级别上是相同的功能,但具体而言,取决于确切的版本,Groovy 可能只是创建临时实现类,而 Java 8 指定了一个完整的机制,包括 lambda Metafactory、lambda factory 和一个涉及invokedynamic 获取 lambda Metafactory。

【讨论】:

    【解决方案3】:

    我想要一个简单的本地闭包来传递/执行。我从来没有在任何地方找到这个答案,所以添加我的旋转,这样你就可以看到从本地 groovy 闭包到本地 java lambda 的实际映射。

    时髦的:

    def clos = {name -> sayHello(name)} 
    clos('John Doe')
    

    Java:

    Consumer<String> clos = name -> sayHello(name);
    clos.accept("John Doe");
    

    关键是要调用的带有accept() 方法的血腥消费者接口——您必须根据Java lambda 的预设函数接口(java.util.function.*) 匹配参数和返回类型。与无类型的 Groovy 版本相比,这有点拖累。

    【讨论】:

      猜你喜欢
      • 2013-09-21
      • 1970-01-01
      • 2014-12-14
      • 2019-11-26
      • 2014-10-15
      • 1970-01-01
      • 2022-11-14
      • 2011-09-24
      • 1970-01-01
      相关资源
      最近更新 更多