Kotlin 类型不匹配:需要数组 <Int?>?但发现 Array<Int>

Kotlin type mismatch: required Array<Int?>? but found Array<Int>

这是我的 Foo 数据 class 定义

data class Foo(
    var fooArg: Array<Int?>? = null, 
)

这是对它的调用:

val bar: Array<Int> = arrayOf(1,2,3)
val foo = Foo(fooArg = bar)

但是这给出了一个错误type mismatch: required Array<Int?>? but found Array<Int>

我很困惑,它需要一个可为 null 的类型,而我为它提供了一个非 null 值,该类型怎么不匹配?

您将 bar 声明为 Array<Int>。非空类型与可空类型不兼容*。将其更改为 Array<Int?> 它将起作用:

val bar: Array<Int?> = arrayOf(1,2,3)
val foo = Foo(fooArg = bar)

或者:

val bar = arrayOf<Int?>(1, 2, 3)

*我认为正确的说法是数组在类型参数上是不变的。但每次我试图正确理解它时,我都会迷路。

Adam 的回答涵盖了解决方案,但也值得一提的是为什么您所做的没有 工作。 Java 使用调用站点差异注释,这与现有的大多数其他语言不同。 Kotlin 采用更传统的方法,即使用声明站点差异注释。这意味着,当声明一个接受泛型参数的 class 时,class 的作者决定它在子类型方面的行为方式。

现在,IntInt? 的子类型。也就是说,每个 Int 都是一个 Int?,但反之则不然。问题是:Array<Int>Array<Int?> 的子类型吗?好吧,List<T> 等协变类型保留子类型,因此 List<Int> 实际上是 List<Int?> 的子类型。如果您将问题中的示例替换为列表而不是数组,则一切正常。

另一方面,Array<T> 是不变类型。我们既可以读取数组也可以写入数组,因此向上转换或向下转换类型参数是不安全的。如果可以的话,下面的内容将进行类型检查。

// Doesn't compile, for good reason
val myHealthyArray: Array<Int> = arrayOf(1);
val myScaryNullableArray: Array<Int?> = myHealthyArray;
myScaryNullableArray[0] = null;

现在我完全无辜的 myHealthyArray 变量中有一个 null 类型 Array<Int> 无法解释。这与 Kotlin 所代表的一切背道而驰,因此原则上是不允许的。

如果你只打算将此数据结构用于阅读,而不是写作,请考虑使用 List<T> 或协变的东西,它能更好地描述类型你的功能,还允许子类型化关系更充分地工作。