Java 8 Lambda构造与JavaScript之间的确切区别是什么?

时间:2016-01-26 10:34:37

标签: javascript java java-8

我必须将Java 8代码转换为JavaScript(单向,一生一次)。为了加快速度,我希望尽可能自动化,然后使用测试套件来修复所有剩余的问题。

我想知道Java 8 lambdas和JavaScript(函数)之间的区别是什么?

任何重要的不兼容性?

2 个答案:

答案 0 :(得分:5)

比较Java lambdas和JS函数时需要注意的一个重要事项是两者如何处理范围。

在Java中,lambdas只能有效地访问最终变量,并且只能捕获那些明确需要的变量。在JS中,函数可以访问所有父闭包中的所有变量,从而捕获所有内容。

除非您知道自己在做什么,否则可能导致内存泄漏。举个例子:

IntSupplier getSupplier(MyMemoryHeavyClass heavy) {
    int x = heavy.hashCode();
    return () -> x;
}

此方法将返回一个实际上只包含int的lambda。这里没问题。但是,对JavaScript的简单翻译......

function getSupplier(heavy) {
    var x = heavy.hashCode();
    return function() { return x; };
}

一目了然可能并不明显,但这有一个重大问题。函数表达式将捕获范围内的所有内容,包括heavy参数(即使它未在返回的函数中引用)。因此,它会阻止heavy(在此示例中需要大量内存)进行垃圾回收,因此只要返回的函数存在,就会保留在内存中。

修改

正如评论中所指出的,这些信息可能有点过时,因为现代引擎似乎更加智能化。例如,V8显然只能捕获它认为必要的任何东西。但是,它仍然可以被欺骗,因为在同一范围内创建的所有函数似乎共享相同的闭包。

例如,添加行(function y() { return heavy; });(否则几乎不做任何操作)会强制heavy进入与return x;函数使用的相同的闭包中,从而产生泄漏。

虽然在这个具体的例子中有些牵强,但是当天真地翻译包含多个lambdas的Java方法时,可能会发生类似的问题,这是不可想象的。

答案 1 :(得分:3)

JavaScript函数/箭头函数表达式与Java 8匿名类/ lambdas之间的一个主要区别是后者(Java)无法重新分配捕获的变量和参数,因为它们considered 实际上是最终的在Java 8中(在以前的Java版本中,所有捕获的变量和参数都必须声明为final)。有一些解决方法可以绕过这个限制:

  • 将自定义Mutable<T>setget方法一起使用,因为Mutable<?>实例可能会作为捕获的变量或参数传递。
  • 使用,比方说AtomicReference<T>或类似的Atomic***方法(如果我是对的,这种方法不能在GWT中使用)。
  • 使用单个元素数组作为值框,因为可以轻松地重新分配数组元素,例如Object[] o = {null}。尽管这种解决方法似乎更低级,并且需要通过索引进行仔细的重新分配/引用,但它可能具有以下优点:1)它完美地处理原始类型(例如,new int[1]绝对优于{{1 }})。 2)一个数组可以包含多个值(比方说,你的lambda表达式可能想要修改捕获的Mutable<Integer>rg,并且三元组可以包含在{{1其中b可以是float[] rgb = new float[3])。如果我是对的话,这种解决方法是IntelliJ IDEA的建议意图。

因此,如果您的Java代码中包含上述变通方法的代码,则可以尝试在移植到JavaScript时删除这些变通方法。比方说,下面的Java代码带有原始数组框

rgb[0]

可以很容易地翻译成更干净的JavaScript(如果您对单独的rfinal float rgb[] = new float[3]; final Runnable whiteOut = () -> { rgb[0] = 255; rgb[1] = 255; rgb[2] = 255; }; final Runnable blackOut = new Runnable() { @Override public void run() { rgb[0] = 255; rgb[1] = 255; rgb[2] = 255; } }; whiteOut.run(); blackOut.run(); r感觉不错):

g