我有一个包含 T 数组(或列表)和一些元数据的接口。
interface DataWithMetadata<T> {
val someMetadata: Int
fun getData(): Array<T>
}
如果我编写接口的最简单实现,则会在 emptyArray()
上出现编译错误:“无法将 T 用作具体化类型参数。请改用类。”
class ArrayWithMetadata<T>(override val someMetadata: Int): DataWithMetadata<T> {
private var myData: Array<T> = emptyArray()
override fun getData(): Array<T> {
return myData
}
fun addData(moreData: Array<T>) {
this.myData += moreData
}
}
但是,如果我将接口和实现都更改为列表,则不会出现编译时问题:
interface DataWithMetadata<T> {
val someMetadata: Int
fun getData(): List<T>
}
class ListWithMetadata<T>(override val someMetadata: Int): DataWithMetadata<T> {
private var myData: List<T> = emptyList()
override fun getData(): List<T> {
return myData
}
fun addData(moreData: Array<T>) {
this.myData += moreData
}
}
我怀疑我的问题中有一些关于 Kotlin 泛型的有趣课程。谁能告诉我编译器在幕后做了什么以及为什么 Array 失败但 List 没有?有没有一种惯用的方法可以在这种情况下编译 Array 实现?
奖励问题:我选择 Array 而不是 List 的唯一原因是我经常看到 Kotlin 开发人员偏爱 Arrays。是这样吗?如果是这样,为什么?
原文由 Nate Vaughan 发布,翻译遵循 CC BY-SA 4.0 许可协议
查看 kotlin stdlib (jvm) 中
emptyArray()
的声明,我们注意到reified
类型参数:reified
类型参数意味着您可以在编译时访问T
的类,并且可以像T::class
一样访问它。您可以在 Kotlin 参考 中阅读有关reified
类型参数的更多信息。由于Array<T>
编译为 javaT[]
,我们需要在编译时知道类型,因此reified
参数如果您尝试编写不带reified
关键字的 emptyArray() 函数,您将收到编译器错误:现在,让我们看一下
emptyList()
的实现:此实现根本不需要参数
T
。它只返回内部对象EmptyList
,它本身继承自List<Nothing>
。 kotlin 类型Nothing
是throw
关键字的返回类型,并且是 _一个永远不存在的值_( 参考)。如果一个方法返回Nothing
,相当于在那个地方抛出异常。所以我们可以在这里安全地使用Nothing
因为每次我们调用EmptyList.get()
编译器都知道这将返回异常。奖金问题:
来自 Java 和 C++,我习惯了
ArrayList
或std::vector
使用这些数组要容易得多。我现在使用 kotlin 几个月了,在编写源代码时,我通常看不出数组和列表之间有什么大的区别。两者都有大量有用的扩展功能,它们的行为方式相似。然而,Kotlin 编译器处理数组和列表的方式非常不同,因为 Java 互操作性对于 Kotlin 团队来说非常重要。我通常更喜欢使用列表,这也是我在你的情况下推荐的。