Scala闭包和Java 8闭包之间的兼容性

24

阅读了一些OpenJDK邮件列表条目后,似乎Oracle开发人员目前正在进一步从闭包提案中删除一些东西,因为Java语言中早期的设计错误使得引入Java闭包变得复杂。

考虑到Scala闭包比计划中的Java 8闭包要强大得多,我想知道是否可能从Scala调用接受闭包的Java方法,在Java中定义一个闭包并将其传递给Scala函数等。

因此,Java闭包是否像它们的Scala对应物一样在字节码中表示,还是不同的呢?能否弥合Java/Scala闭包之间功能差距?


3
我猜想在Java 7发布前不会有确切的答案。 - CheesePls
1
这不是一个真正的问题。你肯定可以从Scala(和其他语言)中使用Java闭包,这对于运行时可操作性是一个好事情。但是Java闭包没有被规定,因此细节还没有确定。 - Thomas Jung
1
@Thomas 你为什么如此确定,Java中的闭包可以从Scala中使用呢?考虑到Scala中的闭包是以类的形式实现的,这是否意味着Java必须使用Scala的FunctionX作为参数类型? - soc
3
@soc - Thomas的意思是,由于所有Java类都可以从Scala中使用,所以新的类也将如此。很可能如此。 - oxbow_lakes
1
@Martin。最初不确定Sun是否计划使用闭包。实际上,它变得越来越确定闭包不会在计划中出现。然后Sun宣布,因为Fork/Join框架需要闭包,所以他们已经把闭包放回了议程中。这发生在去年的某个时候。几周前,Oracle决定让Java 7支持闭包将会延迟发布时间,所以现在Java 7即将发布(大概),而闭包被推迟到Java 8 。 - Ken Bloom
显示剩余2条评论
3个回答

15
我认为这比假设这里有两组利益相关者更加复杂。Lambda 项目的人似乎主要独立于 Oracle 的人工作,偶尔将一些东西抛过围墙,而 Lambda 项目的人会间接发现。 (当然,Scala 是第三方利益相关者。)
由于最新的 Lambda 项目建议是完全消除函数类型,只需要创建某种精巧的推断来实现具有单个抽象方法(SAM 类型)的接口,我预见以下情况:
  • 调用需要Scala闭包的Scala代码将完全取决于Function*特征的实现(以及特征的一般实现)--它是否在Java编译器中显示为SAM(在Scala中是这样的)或者非抽象方法对JVM是否也显得抽象。(我认为,由于特征实现为接口,它们目前看起来像是抽象的,但我对Scala的实现知之甚少。这可能是互操作性的一个巨大障碍。)

    Java泛型的复杂性(特别是如何表示Int/int/IntegerUnit/Nothing/void在通用接口中)也可能会使事情变得复杂。

  • 使用Scala函数实现Java SAM与现在没有任何区别--您需要为要实现的特定接口创建一个implicit转换。

如果JVM获得了函数类型(Oracle似乎并没有排除这种可能性),那么它可能取决于它的实现方式。如果它们是实现特定接口的一等对象,则Scala所需做的就是使Function*实现新接口。如果在JVM中完全实现了一种新类型,则这可能很困难--Scala开发人员可能会像他们目前为Array所做的那样使用魔法来包装它们,或者他们可能会创建implicit转换。(新的语言概念似乎有点牵强。)

我希望所有讨论的结果之一是,所有各种JVM语言都能同意一种标准的方式来表示闭包,这样Scala、Groovy、JRuby等就可以在最少的麻烦下相互传递闭包。
对我而言更有趣的是虚拟扩展方法的提议,它将允许Java集合API使用lambda表达式。根据它们的实现方式,它们可能会大大简化我们在更改Scala代码时必须处理的某些二进制兼容性问题,并且它们可能有助于更轻松、更有效地实现特质。
我希望一些Scala开发者参与并提供他们的意见,但我实际上没有看到任何关于Scala的讨论在Project Lambda列表中,也没有任何参与者让我觉得他们是Scala开发者。

谢谢!你的回答很棒。我个人认为最好的做法是如果Oracle采用Scala的FunctionX类,这样就可以在没有任何魔法的情况下实现互操作性。但我猜他们会尝试任何事情来避免那样做。 - soc
@soc: Scala的FunctionX类适用于许多Scala特定方面的原因:(1)我们的类型系统隐藏了基元和封装类型之间的区别,并可以自然地将void/UnitNothing放入类型系统中。(2)定义位置协变使我们可以仅使用泛型自然地传递FuctionXs,而Java开发人员可能会感到需要在其类型系统中实现一个根本性的新概念。 (我认为这就是为什么Project Lambda放弃了函数类型这个想法的原因。) - Ken Bloom
4
@soc:话虽如此,我对Lambda项目似乎缺乏与其他JVM相关方的讨论感到不安。 - Ken Bloom
Ken,FunctionX的“问题”在于它们不是单一方法,这可能会导致问题。另外,自Scala 2.8.0以来,在Array中没有魔法。 - Daniel C. Sobral
@Daniel,我有点希望像 FunctionX 这样的 trait 可以通过共有的默认方法来实现,然后 JVM(它会知道一些关于共有默认方法的东西)应该会认识到只有一个未实现的方法使得 FunctionX 成为了 SAM。至于 Array:它仍然出现在 Scala 类层次结构中,并且有一些隐式转换来包装它以使用通常的 Seq 方法。如果 JVM 有真正的函数类型,也可以对它们做同样的事情。 - Ken Bloom
@Ken 就像 Int 出现在 Scala 类层次结构中一样,但是现在 Scala 的 Array 完全是 Java 的 Array。当然,有隐式转换,但我不会用“魔法”这个词来形容它,而以前版本的 Scala 中的数组肯定可以用这个词来描述。 - Daniel C. Sobral

7
你很可能可以非常容易地使用隐式转换,例如collection.JavaConversions,无论它们是否是开箱即用的,来完成这个任务。
当然,这并不显然,因为Java闭包可能会被转换成由JVM在运行时生成的类型——我记得Neal Gafter在一个演讲中说过类似的话。

2
注意:5年后,SCALA 2.12.0-M3 (2015年10月) 包含了这个增强功能:

Scala 2.12以与Java 8相同的风格发出闭包

对于每个lambda表达式,编译器会生成一个包含lambda体的方法。
在运行时,该方法作为参数传递给JDK提供的LambdaMetaFactory,该工厂将创建一个闭包对象。

与Scala 2.11相比,新方案的优点是编译器不再为每个lambda表达式生成匿名类。这导致JAR文件显著缩小。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接