由于这是在错误Smart cast to '<type>' is impossible, because '<variable>' is a local variable that is captured by a changing closure的顶部结果中,这里是一个对我有效的通用解决方案(即使闭包不是内联的): 显示此错误的示例:
var lastLine: String? = null
File("filename").useLines {
lastLine = it.toList().last()
}
if(lastLine != null) {
println(lastLine.length) // error! lastLine is captured by the useLines closure above
}
6条答案
按热度按时间w8f9ii691#
一般来说,当一个可变变量在lambda函数闭包中被捕获时,智能强制转换不适用于该变量,无论是在lambda内部还是在创建lambda之后的声明范围内。
这是因为函数可能会从它的封闭作用域中逃逸出来,然后在不同的上下文中执行,可能会多次执行,也可能是并行执行。举个例子,考虑一个假设的函数
List.forEachInParallel { ... }
,它对列表中的每个元素执行给定的lambda函数,但是是并行执行的。编译器必须生成即使在这种严重情况下也保持正确的代码,因此它不会假设变量的值在null检查后保持不变,因此不能智能转换它。
然而,
List.forEach
非常不同,因为它是一个inline
函数。内联函数的主体及其函数参数的主体(除非参数有noinline
或crossinline
修饰符)在调用位置内联,因此编译器可以推断作为参数传递给内联函数的lambda中的代码,就像它直接写在调用方法体中一样。使智能铸造成为可能。它可以,但目前,它没有。只是因为该功能尚未实现。有一个开放的问题:KT-7186。
aiqt4smr2#
感谢Ilya对问题的详细解释!你可以像这样使用标准的
for(item in list){...}
表达式:字符串
hjzp0vay3#
这看起来像是一个编译器bug。
如果
forEach
中的内联lambda参数被标记为crossinline
,那么我将预期编译错误,因为lambda表达式的并发调用的可能性。考虑以下
forEach
实现:字符串
如果没有
crossinline
修饰符,上面的实现将无法编译。没有它,lambda可能包含非本地返回,这意味着它不能以并发方式使用。我建议创建一个问题:Kotlin (KT) | YouTrack。
mefy6pfw4#
问题是
foreach
创建了多个闭包,每个闭包都访问同一个max
,也就是var
。如果在
max == null
检查之后、it > max
之前的另一个闭包中,max
被设置为null
,会发生什么?由于每个闭包理论上都可以独立工作(可能在多个线程上),但都访问相同的
max
,因此您不能保证它在执行期间不会更改。23c0lvtd5#
由于这是在错误
Smart cast to '<type>' is impossible, because '<variable>' is a local variable that is captured by a changing closure
的顶部结果中,这里是一个对我有效的通用解决方案(即使闭包不是内联的):显示此错误的示例:
字符串
Fix:创建一个新的变量,这个变量不被闭包捕获:
型
a0x5cqrl6#
根据您的使用情况,elvis-operator还可以帮助:
字符串
第一个条件(max == null)仅在列表可能确实包含Int.MIN_VALUE作为第一个条目时才是必要的。