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

Posted

技术标签:

【中文标题】Scala 闭包和 Java 8 闭包之间的兼容性【英文标题】:Compatibility between Scala closures and Java 8 closures 【发布时间】:2011-03-04 01:11:29 【问题描述】:

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

考虑到 Scala 闭包比为 Java 8 计划的闭包强大得多,我想知道是否可以 e. G。调用一个 Java 方法,从 Scala 中获取一个闭包,在 Java 中定义一个闭包并将其提供给 Scala 函数等?

那么 Java 闭包会像在字节码中的 Scala 对应物一样表示还是以不同的方式表示? 是否有可能缩小 Java/Scala 闭包之间的功能差距?

【问题讨论】:

我怀疑在接近 Java 7 版本之前我们无法确定 不是一个真正的问题。您当然可以使用来自 Scala(和其他语言)的 Java 闭包,这对于运行时可操作性来说是件好事。但是没有指定 Java 闭包,所以细节还没有。 @Thomas 是什么让你如此确定,Java 闭包可以在 Scala 中使用,考虑到 Scala 闭包是作为类实现的,这并不意味着 Java 必须使用 scala 的 FunnctionX 作为参数类型? @soc - Thomas 只是意味着,因为所有 Java 类都可以从 scala 中使用,所以新的类也可以使用。可能 @Martin。最初并不确定关闭是否在 Sun 的 Java 议程上。 (事实上​​,很确定他们不在议程上)。然后 Sun 宣布将它们列入议程,因为 Fork/Join 框架需要它们。那发生在去年的某个时候。几周前,Oracle 决定在 Java 7 中获得闭包会过多地延迟 Java 7,所以现在 Java 7 将很快发布(ish),并且将闭包推迟到 Java 8。 【参考方案1】:

使用隐式转换 a la collection.JavaConversions,无论它们是否开箱即用,您都可以非常轻松地做到这一点。

当然,显然不是这样,因为有可能是 Java 闭包被转换为由 JVM 在运行时生成的类型 - 我似乎回想一下 Neal Gafter 的演讲,他说了一些类似的话

【讨论】:

【参考方案2】:

我认为这比假设这里有两组利益相关者要复杂得多。 Project Lambda 人员似乎主要独立于 Oracle 人员工作,occasionally throwing something over the wall Project Lambda 人员间接发现。 (Scala,当然是第三个利益相关者。)

由于latest Project Lambda proposal 是为了消除所有函数类型,并且只是为实现具有单一抽象方法(SAM 类型)的接口创建某种奇特的推理,我预见到以下情况:

调用需要 Scala 闭包的 Scala 代码将完全取决于 Function* 特征的实现(以及一般特征的实现)——它在 Java 编译器中是否显示为 SAM(它在 Scala 中-land) 或非抽象方法是否对 JVM 也显得抽象。 (我认为它们目前看起来确实是抽象的,因为特征是作为接口实现的,但我对 Scala 的实现几乎一无所知。这可能是互操作性的一大障碍。)

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

使用 Scala 函数来实现 Java SAM 与现在没有什么不同——您需要为您希望实现的特定接口创建一个 implicit 转换。

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

我希望所有这些讨论的结果之一是所有各种 JVM 语言都将就表示闭包的一些标准方式达成一致——以便 Scala、Groovy、JRuby 等... 都可以以最少的麻烦来回传递闭包。

对我来说更有趣的是virtual extension methods 的提议,它将允许 Java 集合 API 使用 lambdas。根据它们的实现方式,它们可能会极大地简化我们在更改 Scala 代码时必须处理的一些二进制兼容性问题,并且它们可能有助于更轻松、更有效地实现特征。

我希望一些 Scala 开发人员参与进来并提供他们的意见,但我实际上并没有在 Project Lambda 列表中看到任何关于 Scala 的讨论,也没有任何参与者以 Scala 开发人员身份跳出来告诉我。

【讨论】:

谢谢!你的回答很棒。恕我直言,最好的事情是甲骨文只是采用 Scala 的 FunctionX 类,这样事情就可以互操作而没有任何魔法。但我想他们会尝试任何事情来那样做。 @soc:Scala 的 FunctionX 类适用于许多 Scala 特定的原因:(1) 我们的类型系统隐藏了原始类型和盒装类型之间的区别,并且可以适应 void/UnitNothing 自然地进入类型系统。 (2) 定义点协变让我们可以很容易地通过只使用泛型的自然方式传递FuctionXs,Java 开发人员可能会觉得需要在他们的类型系统中实现一个全新的概念来实现这一点。 (我认为这就是为什么 Project Lambda 放弃了函数类型的想法。) @soc:也就是说,我对 Lambda 项目明显缺乏与其他 JVM 支持者的讨论感到不安。 Ken,FunctionX 的“问题”是它们不是单一方法,这可能会导致问题。此外,并不是说自 2.8.0 以来 Scala 中的 Array 就没有魔力了。 @Daniel,我有点希望像FunctionX 这样的特征可以通过公设辩护人方法实现,然后JVM(它会了解公设辩护人方法)应该认识到只有一个未实现的方法制作FunctionX SAM。至于Array:它仍然设法出现在Scala 类层次结构中,并且有隐式转换以使用通常的Seq 方法包装它。如果 JVM 有真正的函数类型,也可以对它们做同样的事情。【参考方案3】:

注意:5 年后,SCALA 2.12.0-M3 (Oct. 2015) 确实包含此增强功能:

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

对于每个 lambda,编译器都会生成一个包含 lambda 主体的方法。 在运行时,这个方法作为参数传递给 JDK 提供的LambdaMetaFactory,它会创建一个闭包对象。

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

【讨论】:

以上是关于Scala 闭包和 Java 8 闭包之间的兼容性的主要内容,如果未能解决你的问题,请参考以下文章

闭包与内部类

scala编程——函数和闭包

函数和闭包

Scala 闭包

scala 闭包的概念

Scala 闭包