编译错误:智能转换为”是不可能的,因为”是一个局部variables,

为了简化我的实际使用情况,我们假设我想在列表中find最大数字:

var max : Int? = null listOf(1, 2, 3).forEach { if (max == null || it > max) { max = it } } 

但是,编译失败,出现以下错误:

智能转换为’Int’是不可能的,因为’max’是一个由变化的闭包捕获的局部variables

为什么更改的闭包会阻止智能投射在这个例子中工作?

一般情况下,当在lambda函数闭包中捕获一个可变variables时,在创建lambda之后,在lambdaexpression式和声明范围内,smart转换都不适用于该variables。

这是因为该函数可能会从其封闭的范围中逃脱,并可能稍后在不同的上下文中执行,可能多次并且可能并行执行。 作为一个例子,考虑一个假设的函数List.forEachInParallel { ... } ,它为列表的每个元素执行给定的lambda函数,但并行执行。

编译器必须生成代码,即使在严重的情况下也会保持正确,所以它不会假设variables的值在空检查后保持不变,因此不能对其进行智能转换。

List.forEach是有点不同,因为它是一个inline函数。 内联函数的主体及其函数参数的主体(除非参数具有noinlinecrossinline修饰符)被内联到调用位置,因此编译器可以crossinline lambda中的代码作为parameter passing给内联函数,就好像它直接写在调用方法体内,使智能转换成为可能。

它可以,但目前它不,只是因为该function尚未实现。 有一个公开的问题: KT-7186 。

这看起来像一个编译器的bug。

如果forEach中的inline lambda参数被标记为crossinline那么由于lambdaexpression式的并发调用的可能性,我期望编译错误。

考虑以下forEach实现:

 inline fun  Iterable.forEach(crossinline action: (T) -> Unit): Unit { val executorService: ExecutorService = ForkJoinPool.commonPool() val futures = map { element -> executorService.submit { action(element) } } futures.forEach { future -> future.get() } } 

如果没有crossinline修饰符,上面的实现将会失败。 没有它,lambda可能包含非本地返回 ,这意味着它不能以并发方式使用。

我建议创建一个问题: Kotlin(KT)| YouTrack 。

问题是foreach创建了多个闭包,每个闭包访问相同的max var

如果在max == null检查之后但在it > max之前it > max max被设置为null的另一个闭包中应该发生什么?

由于每个闭包在理论上可以独立工作(可能在多个线程中),但是所有访问的访问都是相同的max ,所以不能保证在执行期间它不会改变。

感谢伊利亚对问题的详细解释! 如果你需要一个解决方法,你可以像这样使用标准for(item in list){...}expression式:

  var max : Int? = null val list = listOf(1, 2, 3) for(item in list){ if (max == null || item > max) { max = item } }