2017-10-22 85 views
6

我有一个包含T和一些元数据的数组(或列表)的接口。Kotlin generics Array <T> results in“Can not use T as a reified type parameter。Use a class instead”但List <T> does not

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 
    } 
} 

我怀疑有在科特林泛型一些有趣的课我的问题里面。任何人都可以告诉我编译器在底层做什么,为什么Array失败,但List不?在这种情况下是否有一种习惯方式来编译数组实现?

奖金问题:我在List上列出的Array的唯一原因是我经常看到Kotlin开发人员喜欢阵列。是这样,如果是这样,为什么?

回答

5

综观emptyArray()在科特林STDLIB(JVM)的声明中,我们注意到reified类型参数:

public inline fun <reified @PureReifiable T> emptyArray(): Array<T> 

reified类型参数意味着你必须在编译时访问类的T并可以像T::class那样访问它。您可以在Kotlin reference中阅读关于reified类型参数的更多信息。由于Array<T>编译为java T[],因此我们需要知道编译时的类型,因此需要了解reified参数。如果您尝试没有reified关键字写一个emptyArray()函数,你会得到一个编译器错误:

fun <T> emptyArray() : Array<T> = Array(0, { throw Exception() }) 

Cannot use T as a reified type parameter. Use a class instead.


现在,让我们来看看在emptyList()实现:

public fun <T> emptyList(): List<T> = EmptyList 

该实现完全不需要参数T。它只是返回内部对象EmptyList,它本身继承自List<Nothing>。 kotlin类型Nothing是关键字throw的返回类型,并且是从不存在的值reference)。如果某个方法返回Nothing,则等同于在该位置抛出异常。所以我们可以在这里安全地使用Nothing,因为每当我们调用EmptyList.get()时,编译器知道这会返回一个异常。


奖金的问题:

从Java和C++的到来,我习惯了ArrayListstd::vector是更容易使用,数组。我现在使用kotlin几个月,在编写源代码时,我通常看不到数组和列表之间的巨大差异。两者都有许多有用的扩展函数,其行为方式类似。但是,Kotlin编译器处理数组和列表的方式非常不同,因为Java互操作性对于Kotlin团队非常重要。我通常更喜欢使用列表,这也是我的建议。

2

的问题是,一个Array必须在知道的通用元素类型的编译时间,这是由reified类型参数指示在这里,因为在申报看到:

public inline fun <reified @PureReifiable T> emptyArray(): Array<T> 

这只是可能会创建像Array<String>Array<Int>但不是Array<T>类型的混凝土阵列。

在此answer中,您可以找到几种解决方法。希望你找到一个合适的方法。

相关问题