Kotlin 泛型 Array<T> 导致“不能使用 T 作为重新定义的类型参数。请改用类“,但 List<T> 则不然

2022-08-31 17:20:33

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

interface DataWithMetadata<T> {
    val someMetadata: Int
    fun getData(): Array<T>
}

如果我编写接口的最简单的实现,我会在:“不能使用T作为重新初始化的类型参数。请改用类。emptyArray()

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 泛型中有一些有趣的教训。谁能告诉我编译器在引擎盖下做了什么,为什么数组失败了,但List没有?有没有一种习惯用语的方法让 Array 实现在此上下文中编译?

奖励问题:我选择 Array over List 的唯一原因是我经常看到 Kotlin 开发人员喜欢 Arrays。情况是否如此,如果是,为什么?


答案 1

查看 kotlin stdlib (jvm) 中的声明,我们注意到 type 参数:emptyArray()reified

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

type 参数意味着您可以在编译时访问 的类,并且可以像 访问它一样。您可以在 Kotlin 参考中阅读有关类型参数的更多信息。由于编译为 java ,我们需要在编译时知道类型,因此参数。如果您尝试编写一个没有关键字的 emptyArray() 函数,则会收到编译器错误:reifiedTT::classreifiedArray<T>T[]reifiedreified

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

不能使用 T 作为重新定义类型参数。请改用类。


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

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

此实现根本不需要该参数。它只是返回内部对象,该对象本身继承自 。kotlin 类型是关键字的返回类型,是一个永远不存在的值引用)。如果一个方法返回,则等效于在该位置抛出一个异常。因此,我们可以安全地在此处使用,因为每次调用编译器时都知道这将返回异常。TEmptyListList<Nothing>NothingthrowNothingNothingEmptyList.get()


奖金问题:

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


答案 2

问题在于,必须在编译时知道 的泛型类型,这由此处的 type 参数指示,如声明所示:Arrayreified

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

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

在此答案中,您可以找到几种解决方法。