首页 文章

Kotlin泛型Array <T>导致“不能将T用作具体类型参数 . 使用类代替“但List <T>不会

提问于
浏览
21

我有一个包含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 回答

  • 15

    查看kotlin stdlib(jvm)中 emptyArray() 的声明,我们注意到 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() })
    

    不能使用T作为具体类型参数 . 请改用一个 class .


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

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

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


    Bonus question:

    来自Java和C,我习惯 ArrayListstd::vector 更容易使用该数组 . 我现在使用kotlin几个月,我通常也不会在你的情况下推荐我 .

  • 0

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

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

    它只能创建像 Array<String>Array<Int> 这样的具体数组,但不能创建 Array<T> 类型的具体数组 .

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

  • 5

    对我来说最有效的解决方法是:

    @Suppress("UNCHECKED_CAST")
    var pool: Array<T?> = arrayOfNulls<Any?>(initialCapacity) as Array<T?>
    

相关问题