我必须将Java 8代码转换为JavaScript(单向,一生一次)。为了加快速度,我希望尽可能自动化,然后使用测试套件来修复所有剩余的问题。
我想知道Java 8 lambdas和JavaScript(函数)之间的区别是什么?
任何重要的不兼容性?
答案 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>
与set
和get
方法一起使用,因为Mutable<?>
实例可能会作为捕获的变量或参数传递。AtomicReference<T>
或类似的Atomic***
方法(如果我是对的,这种方法不能在GWT中使用)。Object[] o = {null}
。尽管这种解决方法似乎更低级,并且需要通过索引进行仔细的重新分配/引用,但它可能具有以下优点:1)它完美地处理原始类型(例如,new int[1]
绝对优于{{1 }})。 2)一个数组可以包含多个值(比方说,你的lambda表达式可能想要修改捕获的Mutable<Integer>
,r
和g
,并且三元组可以包含在{{1其中b
可以是float[] rgb = new float[3]
)。如果我是对的话,这种解决方法是IntelliJ IDEA的建议意图。因此,如果您的Java代码中包含上述变通方法的代码,则可以尝试在移植到JavaScript时删除这些变通方法。比方说,下面的Java代码带有原始数组框
rgb[0]
可以很容易地翻译成更干净的JavaScript(如果您对单独的r
,final 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