编译错误:智能转换为'<type>'是不可能的,因为'<variable>'是一个由更改的闭包捕获的局部变量

Dra*_*vic 18 casting kotlin

为了简化我的实际用例,我们假设我想在列表中找到最大数量:

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

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

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

为什么更改闭包会阻止智能演员在此示例中工作?

Ily*_*lya 18

通常情况下,当在lambda函数闭包中捕获可变变量时,智能强制转换不适用于lambda内部和声明范围创建lambda之后的变量.

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

编译器必须生成即使在那种严重情况下仍然保持正确的代码,因此它不会假设变量的值在空检查后保持不变,因此无法智能地转换它.

但是List.forEach有点不同,因为它是一种inline功能.内联函数的主体及其功能参数的主体(除非参数有noinlinecrossinline修饰符)在调用站点内联,因此编译器可以推断作为内联函数的参数传递的lambda中的代码,就像它被写入一样直接在调用方法体中使智能投射成为可能.

它可以,但目前它没有,只是因为该功能尚未实现.有一个未解决的问题:KT-7186.


Ivo*_*nov 7

感谢Ilya对问题的详细解释!您可以for(item in list){...}像这样使用标准表达式:

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

  • 这救了我的命,可惜我不能两次投票!恕我直言,它实际上不应该被称为解决方法,因为它意味着“for(list in list) {...}”是某种反模式,在我看来它不存在。(是的,在编码方面我有点老派。) (3认同)