Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/kotlin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Kotlin 编译错误:智能转换为'&书信电报;类型>';这是不可能的,因为'&书信电报;变量>';是由不断变化的闭包捕获的局部变量_Kotlin_Casting - Fatal编程技术网

Kotlin 编译错误:智能转换为'&书信电报;类型>';这是不可能的,因为'&书信电报;变量>';是由不断变化的闭包捕获的局部变量

Kotlin 编译错误:智能转换为'&书信电报;类型>';这是不可能的,因为'&书信电报;变量>';是由不断变化的闭包捕获的局部变量,kotlin,casting,Kotlin,Casting,为了简化我的实际用例,让我们假设我想在列表中找到最大数量: var max : Int? = null listOf(1, 2, 3).forEach { if (max == null || it > max) { max = it } } 但是,编译失败,出现以下错误: 智能转换为“Int”是不可能的,因为“max”是一个局部变量,由不断变化的闭包捕获 为什么更改闭包会阻止smart cast在此示例中工作?问题在于foreach创建多个闭包,每个闭包

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

var max : Int? = null
listOf(1, 2, 3).forEach {
    if (max == null || it > max) {
        max = it
    }
}
但是,编译失败,出现以下错误:

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


为什么更改闭包会阻止smart cast在此示例中工作?

问题在于
foreach
创建多个闭包,每个闭包访问相同的
max
,这是一个
var

如果在
max==null
检查之后但在
it>max
之前的另一个闭包中将
max
设置为
null
,会发生什么情况


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

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

这是因为该函数可能会从其封闭范围中逃逸出来,并可能稍后在不同的上下文中执行,可能会多次执行,也可能并行执行。作为一个例子,考虑一个假设函数<代码> List.PoaAcQuin并行{…} /Cult>,它为列表中的每个元素执行给定的lambda函数,但并行。

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

但是,
List.forEach
则完全不同,因为它是一个函数。内联函数体及其函数参数体(除非参数具有
noinline
crossinline
修饰符)在调用站点内联,因此,编译器可以对作为参数传递给内联函数的lambda中的代码进行推理,就好像它是直接写在调用方法体中的一样,从而使智能转换成为可能


它可以,但目前没有。这仅仅是因为该功能尚未实现。它有一个公开的问题:。

这在我看来像是一个编译器错误

如果
forEach
中的内联lambda参数被标记为
crossinline
,那么由于lambda表达式的并发调用的可能性,我预计会出现编译错误

考虑以下
forEach
实现:

inline fun <T> Iterable<T>.forEach(crossinline action: (T) -> Unit): Unit {
    val executorService: ExecutorService = ForkJoinPool.commonPool()
    val futures = map { element -> executorService.submit { action(element) } }
    futures.forEach { future -> future.get() }
}
inlinefuniterable.forEach(交叉内联动作:(T)->Unit):单位{
val executorService:executorService=ForkJoinPool.commonPool()
val futures=map{element->executorService.submit{action(element)}
futures.forEach{future->future.get()}
}
如果没有
crossinline
修饰符,上述实现将无法编译。如果没有它,lambda可能包含,这意味着它不能以并发方式使用

我建议创建一个问题:。

感谢您对问题的详细解释! 您可以使用标准的
for(列表中的项){…}
表达式,如下所示:

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

forEach
的主体不应该内联吗?在这种情况下,
max
应该只是一个局部变量,因此多线程不是问题。如果
forEach
的主体生成线程,多线程仍然是问题。内联线程的创建并不内联线程。确切地说,这正是我首先困惑的地方。如果一个lambda主体是完全内联的,那么如果我手动编写内联代码的话,我希望我能做所有我想做的事情。这节省了我的时间,糟糕的是,我不能再投票了!而且它实际上不应该被称为变通方法IMHO,因为它意味着“for(list中的项){…}”是某种反模式,在我看来是不存在的。(是的,我在编码方面有些老派。)谢谢你的评论@IllvilJa!我修正了描述。