Kotlin泛型Array <T>导致"不能使用T作为一个改进的类型参数.请改用类"但List <T>不会

Nat*_*han 34 java arrays generics reification kotlin

我有一个包含T和一些元数据的数组(或列表)的接口.

interface DataWithMetadata<T> {
    val someMetadata: Int
    fun getData(): Array<T>
}
Run Code Online (Sandbox Code Playgroud)

如果我编写最简单的接口实现,我会得到一个编译错误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
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,如果我将接口和实现都更改为列表,我没有编译时问题:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

我怀疑在我的问题中Kotlin泛型有一些有趣的教训.任何人都可以告诉我编译器在幕后做了什么以及为什么Array失败但List没有?是否有一种惯用的方法可以在此上下文中编译Array实现?

奖金问题:我达到Array over List的唯一原因是我经常看到Kotlin开发人员喜欢Arrays.是这种情况,如果是这样,为什么?

msr*_*rd0 24

看一下emptyArray()kotlin stdlib(jvm)中的声明,我们注意到了reifiedtype参数:

public inline fun <reified @PureReifiable T> emptyArray(): Array<T>
Run Code Online (Sandbox Code Playgroud)

reified类型参数意味着你可以访问类T在编译时,可以像访问它T::class.您可以reifiedKotlin参考中阅读有关类型参数的更多信息.由于Array<T>编译为java T[],我们需要在编译时知道类型,因此reified参数.如果您尝试编写没有reified关键字的emptyArray()函数,您将收到编译器错误:

fun <T> emptyArray() : Array<T> = Array(0, { throw Exception() })
Run Code Online (Sandbox Code Playgroud)

不能使用T作为reified类型参数.请改用一个班级.


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

public fun <T> emptyList(): List<T> = EmptyList
Run Code Online (Sandbox Code Playgroud)

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


奖金问题:

来自Java和C++,我习惯ArrayList或者std::vector更容易使用那些数组.我现在使用kotlin几个月,在编写源代码时,我通常看不到数组和列表之间的巨大差异.两者都有大量有用的扩展函数,它们以类似的方式运行.但是,Kotlin编译器处理的数组和列表非常不同,因为Java互操作性对于Kotlin团队非常重要.我通常更喜欢使用列表,这也是我在你的情况下推荐的.


s1m*_*nw1 7

问题是Array必须在编译时知道an的泛型类型,这由reified类型参数在此处指示,如声明中所示:

public inline fun <reified @PureReifiable T> emptyArray(): Array<T>
Run Code Online (Sandbox Code Playgroud)

只能创建类似于Array<String>Array<Int>不是类型的具体数组Array<T>.

在这个答案中,您可以找到几种解决方法.希望你找到合适的方式.