Kotlin 泛型 Array<T> 结果为 "Cannot use T as a reified type parameter. Use a class instead" 但 List<T> 没有
Kotlin generics Array<T> results in "Cannot use T as a reified type parameter. Use a class instead" but List<T> does not
我有一个包含 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 而不是 List 的唯一原因是我经常看到 Kotlin 开发人员偏爱 Arrays。是这样吗,如果是,为什么?
查看kotlin stdlib(jvm)中emptyArray()
的声明,我们注意到reified
类型参数:
public inline fun <reified @PureReifiable T> emptyArray(): Array<T>
reified
类型参数意味着你可以在编译时访问 T
的 class 并且可以像 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
。它只是 return 内部对象 EmptyList
,它本身继承自 List<Nothing>
。 kotlin 类型 Nothing
是 throw
关键字的 return 类型,并且是 永远不存在的值 (reference)。如果一个方法returns Nothing
,就相当于在那个地方抛出异常。所以我们可以在这里安全地使用 Nothing
因为每次我们调用 EmptyList.get()
编译器都知道这将 return 一个异常。
加分题:
来自 Java 和 C++,我习惯了 ArrayList
或 std::vector
以便更容易使用该数组。我现在使用 kotlin 几个月了,在编写源代码时,我通常看不出数组和列表之间有什么大的区别。两者都有大量有用的扩展功能,它们的行为方式相似。然而,Kotlin 编译器处理数组和列表的方式非常不同,因为 Java 互操作性对于 Kotlin 团队来说非常重要。我通常更喜欢使用列表,这也是我在你的情况下推荐的。
问题是 Array
的泛型类型必须在 编译时 已知,这由此处的 reified
类型参数指示,如声明中所示:
public inline fun <reified @PureReifiable T> emptyArray(): Array<T>
只能创建像 Array<String>
或 Array<Int>
这样的具体数组,但不能创建 Array<T>
.
类型的数组
在此 中,您可以找到几种解决方法。
最适合我的解决方法是:
@Suppress("UNCHECKED_CAST")
var pool: Array<T?> = arrayOfNulls<Any?>(initialCapacity) as Array<T?>
我在尝试 return T 时得到了 Type parameter T cannot be called as function
。
private fun <T> getData(): T {
return T()
}
见What is the proper way to create new instance of generic class in kotlin?:
private fun <T> create(
method: (Int) -> T,
value: Int
): T {
return method(value) // Creates T(value).
}
// Usage:
create(::ClassName, 1)
其中 ClassName
扩展 T
.
也许这会有所帮助:
private inline fun <reified T> getData(): T {
return T::class.java.newInstance()
}
但在我的情况下,我不得不 return T(parameter)
,而不是 T()
,所以,没有尝试。另见 .
我在上面的解决方案中遇到了一些问题。这是我使用 typeOf
from kotlin-reflect:
想出的
@Suppress("UNCHECKED_CAST")
private inline fun <reified T> createArrayOfGeneric(): Array<T> {
return java.lang.reflect.Array.newInstance(typeOf<T>().javaType as Class<*>, 10) as Array<T>
}
newInstance
方法将java类型作为从typeOf
得到的泛型的class,第二个参数是数组的长度。
我有一个包含 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 而不是 List 的唯一原因是我经常看到 Kotlin 开发人员偏爱 Arrays。是这样吗,如果是,为什么?
查看kotlin stdlib(jvm)中emptyArray()
的声明,我们注意到reified
类型参数:
public inline fun <reified @PureReifiable T> emptyArray(): Array<T>
reified
类型参数意味着你可以在编译时访问 T
的 class 并且可以像 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
。它只是 return 内部对象 EmptyList
,它本身继承自 List<Nothing>
。 kotlin 类型 Nothing
是 throw
关键字的 return 类型,并且是 永远不存在的值 (reference)。如果一个方法returns Nothing
,就相当于在那个地方抛出异常。所以我们可以在这里安全地使用 Nothing
因为每次我们调用 EmptyList.get()
编译器都知道这将 return 一个异常。
加分题:
来自 Java 和 C++,我习惯了 ArrayList
或 std::vector
以便更容易使用该数组。我现在使用 kotlin 几个月了,在编写源代码时,我通常看不出数组和列表之间有什么大的区别。两者都有大量有用的扩展功能,它们的行为方式相似。然而,Kotlin 编译器处理数组和列表的方式非常不同,因为 Java 互操作性对于 Kotlin 团队来说非常重要。我通常更喜欢使用列表,这也是我在你的情况下推荐的。
问题是 Array
的泛型类型必须在 编译时 已知,这由此处的 reified
类型参数指示,如声明中所示:
public inline fun <reified @PureReifiable T> emptyArray(): Array<T>
只能创建像 Array<String>
或 Array<Int>
这样的具体数组,但不能创建 Array<T>
.
在此
最适合我的解决方法是:
@Suppress("UNCHECKED_CAST")
var pool: Array<T?> = arrayOfNulls<Any?>(initialCapacity) as Array<T?>
我在尝试 return T 时得到了 Type parameter T cannot be called as function
。
private fun <T> getData(): T {
return T()
}
见What is the proper way to create new instance of generic class in kotlin?:
private fun <T> create(
method: (Int) -> T,
value: Int
): T {
return method(value) // Creates T(value).
}
// Usage:
create(::ClassName, 1)
其中 ClassName
扩展 T
.
也许这会有所帮助:
private inline fun <reified T> getData(): T {
return T::class.java.newInstance()
}
但在我的情况下,我不得不 return T(parameter)
,而不是 T()
,所以,没有尝试。另见
我在上面的解决方案中遇到了一些问题。这是我使用 typeOf
from kotlin-reflect:
@Suppress("UNCHECKED_CAST")
private inline fun <reified T> createArrayOfGeneric(): Array<T> {
return java.lang.reflect.Array.newInstance(typeOf<T>().javaType as Class<*>, 10) as Array<T>
}
newInstance
方法将java类型作为从typeOf
得到的泛型的class,第二个参数是数组的长度。