我有一个包含T和一些元数据的数组(或列表)的接口 .
interface DataWithMetadata<T> {
val someMetadata: Int
fun getData(): Array<T>
}
如果我编写最简单的接口实现,我会在 emptyArray()
上收到编译错误:"Cannot use T as a reified type parameter. Use a class instead."
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 over List的唯一原因是我经常看到Kotlin开发人员喜欢Arrays . 是这种情况,如果是这样,为什么?
3 回答
查看kotlin stdlib(jvm)中
emptyArray()
的声明,我们注意到reified
类型参数:reified
类型参数意味着您可以在编译时访问T
类,并且可以像T::class
一样访问它 . 您可以在Kotlin reference中阅读有关reified
类型参数的更多信息 . 由于Array<T>
编译为javaT[]
,我们需要在编译时知道类型,因此reified
参数 . 如果您尝试在没有reified
关键字的情况下编写emptyArray()函数,则会出现编译器错误:现在,我们来看看
emptyList()
的实现:此实现根本不需要参数
T
. 它只返回内部对象EmptyList
,它本身继承自List<Nothing>
. kotlin类型Nothing
是throw
关键字的返回类型,是一个永不存在的值(reference) . 如果方法返回Nothing
,则相当于在该位置抛出异常 . 所以我们可以安全地在这里使用Nothing
,因为每次我们调用EmptyList.get()
时,编译器都会知道这将返回异常 .Bonus question:
来自Java和C,我习惯
ArrayList
或std::vector
更容易使用该数组 . 我现在使用kotlin几个月,我通常也不会在你的情况下推荐我 .问题是
Array
的泛型类型必须在编译时知道,这在此处由reified
类型参数指示,如声明中所示:它只能创建像
Array<String>
或Array<Int>
这样的具体数组,但不能创建Array<T>
类型的具体数组 .在这个answer中,您可以找到几种解决方法 . 希望你找到合适的方式 .
对我来说最有效的解决方法是: