Kotlin 泛型 Array<T> 导致“不能将 T 用作具体化类型参数。改用类”,但 List<T> 不会

新手上路,请多包涵

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

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

我怀疑我的问题中有一些关于 Kotlin 泛型的有趣课程。谁能告诉我编译器在幕后做了什么以及为什么 Array 失败但 List 没有?有没有一种惯用的方法可以在这种情况下编译 Array 实现?

奖励问题:我选择 Array 而不是 List 的唯一原因是我经常看到 Kotlin 开发人员偏爱 Arrays。是这样吗?如果是这样,为什么?

原文由 Nate Vaughan 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 400
2 个回答

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

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

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

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

不能将 T 用作具体化类型参数。请改用类。


现在,让我们看一下 emptyList() 的实现:

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

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


奖金问题:

来自 Java 和 C++,我习惯了 ArrayListstd::vector 使用这些数组要容易得多。我现在使用 kotlin 几个月了,在编写源代码时,我通常看不出数组和列表之间有什么大的区别。两者都有大量有用的扩展功能,它们的行为方式相似。然而,Kotlin 编译器处理数组和列表的方式非常不同,因为 Java 互操作性对于 Kotlin 团队来说非常重要。我通常更喜欢使用列表,这也是我在你的情况下推荐的。

原文由 msrd0 发布,翻译遵循 CC BY-SA 4.0 许可协议

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

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

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

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

原文由 s1m0nw1 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题