我正在阅读"Kotlin 编程 - 大书牧场指南"一书中有关 Kotlin 中进出关键字的部分。我来自一个C++背景,初始化和赋值是两个非常不同的概念。书中的以下代码片段(略有修改(让我承认。
代码如下:
class Barrel<out T>(val item:T)
open class Loot(val value: Int)
class Fedora(val name:String, value:Int) : Loot(value)
public fun main(){
var fedoraBarrel: Barrel<Fedora> = Barrel(Fedora("a generic-looking fedora", 15))
var lootBarrel: Barrel<Loot> = fedoraBarrel
lootBarrel = fedoraBarrel
val myFedora: Fedora = lootBarrel.item
}
在这里,当我注释掉这一行时
lootBarrel = fedoraBarrel
我在尝试从桶中检索Fedora项目的后续行上收到以下错误,错误是
Error:(27, 28) Kotlin: Type mismatch: inferred type is Loot but Fedora was expected
将 fedoraBarrel 分配给 lootBarrel 与使用 fedoraBarrel 初始化 lootBarrel 有何不同。为什么我需要这条线
lootBarrel = fedoraBarrel
要编译此代码?
为什么这条线
var lootBarrel: Barrel<Loot> = fedoraBarrel
没有意义?
它不是没有意义的,恰恰相反;你明确要求更通用的类型, 编译器合理地假设你真的希望lootBarrel
有类型Barrel<Loot>
,所以lootBarrel.item
有类型Loot
,而不是Fedora
。
后
lootBarrel = fedoraBarrel
编译器看到它被分配了一个类型为Barrel<Fedora>
的值,因此可以在本地将其视为具有该类型,直到它被更改。
不重新分配它能工作吗?是的。在 https://youtrack.jetbrains.com/issue/KT-13663 讨论,目前的结论是
我认为不可能在所有情况下合理地实现此请求,我们需要设计一种棘手的算法来使其至少在大多数情况下起作用。
在您的情况下,最后的评论给出了解决方法
var lootBarrel: Barrel<Loot>
lootBarrel = fedoraBarrel
val myFedora: Fedora = lootBarrel.item
(注意这是初始化,不是赋值(
让我们将您的情况简化为以下代码:
var a: Number = 0
a + 1 // Raises an error, class Number does not have `plus` function
a = 0
a + 1 // Works fine
初始化变量时 (var a: Number = 0
( 编译器将a
的类型设置为Number
,而不考虑用作初始值的表达式类型(它不会将a
转换为Int
,因为您显式指定了类型(。
当您编写a = 0
时,会发生智能转换,因此您可以像使用Int
一样使用a
,直到再次更改a
。