我有一个方法,它返回一个只打印订单号的简单函数数组:
def buggyClosureFunction(amount: Int) = {
val functions = new Array[() => Unit](amount);
var i = 0;
while (i < amount) {
functions(i) = {()=>print(i + " ")}
i += 1;
}
functions
}
val wow = buggyClosureFunction(4);
wow.foreach(_());
这打印4 4 4 4.(所有函数打印它们引用的i
的值。
我似乎无法更改我的方法,以便返回的函数将打印1 2 3 4.
我的一次尝试:
def goodClosureFunction(amount: Int) = {
val functions: = new Array[() => Unit](amount);
var i = 0;
val helper = (num: Int) => {
print(num);
}
while (i < amount) {
functions(i) = {()=>helper(i)()}
i += 1;
}
functions
}
但是这个stil打印4 4 4 4。
答案 0 :(得分:3)
当您关闭var i = 0
时,Scala编译器会将i
从scala.Int
变为scala.runtime.IntRef
并将其提升到编译器生成的类中。这意味着生成的类保存对所述类型的引用。执行该函数时,该值实际上是指向最后一个值,而不是保持每次迭代的值。
为了避免这种情况,请在封闭内创建i
的本地副本:
while (i < amount) {
functions(i) = {
val j = i
() => print(j + " ")
}
i += 1;
}
正如@Łukasz指出的那样,如果你想采用功能方法并避免关闭麻烦和可变状态:
def nonBuggyNoClosure(n: Int) = (0 until n) map (i => () => print(i + " "))
中Scala闭包的实现细节的更多信息