java Kotlin 泛型 Array<T> 导致“不能使用 T 作为具体化的类型参数。改用类”但 List<T> 不会
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/46869353/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Kotlin generics Array<T> results in "Cannot use T as a reified type parameter. Use a class instead" but List<T> does not
提问by Nate Vaughan
I have an interface that contains an array (or list) of T and some metadata.
我有一个接口,其中包含 T 的数组(或列表)和一些元数据。
interface DataWithMetadata<T> {
val someMetadata: Int
fun getData(): Array<T>
}
If I write the simplest implementation of the interface, I get a compile error on the emptyArray()
: "Cannot use T as a reified type parameter. Use a class instead."
如果我编写接口的最简单实现,则会出现编译错误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
}
}
However, if I change both the interface and the implementation to a list, I have no compile-time issues:
但是,如果我将接口和实现都更改为列表,则没有编译时问题:
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
}
}
I suspect there is some interesting lesson in Kotlin generics inside my issue. Can anyone tell me what the compiler is doing under the hood and why Array fails but List does not? Is there an idiomatic way to make the Array implementation compile in this context?
我怀疑我的问题中有一些关于 Kotlin 泛型的有趣教训。谁能告诉我编译器在幕后做了什么以及为什么 Array 失败而 List 没有?在这种情况下,是否有一种惯用的方法可以使 Array 实现编译?
Bonus question: The only reason I reached for Array over List is that I often see Kotlin developers favor Arrays. Is this the case, and if so, why?
额外问题:我选择 Array 而不是 List 的唯一原因是我经常看到 Kotlin 开发人员喜欢 Arrays。是这样吗,如果是,为什么?
采纳答案by msrd0
Looking at the declaration of emptyArray()
in the kotlin stdlib (jvm), we notice the reified
type parameter:
查看emptyArray()
kotlin stdlib (jvm)中的声明,我们注意到reified
类型参数:
public inline fun <reified @PureReifiable T> emptyArray(): Array<T>
The reified
type parameter means that you have access to the class of T
at compile-time and can access it like T::class
. You can read more about reified
type parameters in the Kotlin reference. Since Array<T>
compiles to java T[]
, we need to know the type at compile-time, hence the reified
parameter. If you try writing an emptyArray() function without the reified
keyword, you'll get a compiler error:
该reified
类型参数意味着你必须访问类的T
在编译时,可以像访问它T::class
。您可以reified
在Kotlin 参考 中阅读有关类型参数的更多信息。由于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.
不能使用 T 作为具体化的类型参数。改用类。
Now, let's take a look at the implementation of emptyList()
:
现在,让我们来看看 的实现emptyList()
:
public fun <T> emptyList(): List<T> = EmptyList
This implementation doesn't need the parameter T
at all. It just returns the internal object EmptyList
, which itself inherits from List<Nothing>
. The kotlin type Nothing
is the return-type of the throw
keyword and is a value that never exists(reference). If a method returns Nothing
, is is equivalent to throwing an exception at that place. So we can safely use Nothing
here because everytime we would call EmptyList.get()
the compiler knows that this will return an exception.
这个实现根本不需要参数T
。它只返回内部对象EmptyList
,它本身继承自List<Nothing>
. kotlin 类型Nothing
是throw
关键字的返回类型,是一个永远不存在的值(引用)。如果一个方法返回Nothing
,就相当于在那个地方抛出异常。所以我们可以安全地使用Nothing
这里,因为每次我们调用EmptyList.get()
编译器时都知道这将返回一个异常。
Bonus question:
奖金问题:
Coming from Java and C++, I am used to ArrayList
or std::vector
to be far easier to use that arrays. I use kotlin now for a few month and I usually don't see a big difference between arrays and lists when writing source code. Both have tons of usefull extension functions which behave in a similar way. However, the Kotlin compiler handles arrays and lists very different, as Java interoperability is very important for the Kotlin team. I usually prefer using lists, and that's what I'd recommend in your case too.
来自 Java 和 C++,我习惯于ArrayList
或std::vector
更容易使用这些数组。我现在使用 kotlin 几个月了,在编写源代码时,我通常看不到数组和列表之间的大区别。两者都有大量有用的扩展函数,它们的行为方式相似。然而,Kotlin 编译器处理数组和列表的方式非常不同,因为 Java 互操作性对 Kotlin 团队来说非常重要。我通常更喜欢使用列表,这也是我在您的情况下推荐的。
回答by s1m0nw1
The problem is that the generic type of an Array
must be known at compile time, which is indicated by the reified
type parameter here, as seen in the declaration:
问题是 an 的泛型类型Array
必须在编译时知道,reified
这里的类型参数表示,如声明中所示:
public inline fun <reified @PureReifiable T> emptyArray(): Array<T>
It's only possible to create concrete arrays like Array<String>
or Array<Int>
but not of type Array<T>
.
只能创建像Array<String>
orArray<Int>
但不是 type 的具体数组Array<T>
。
In this answer, you can find several workarounds.
在这个答案中,您可以找到几种解决方法。
回答by Riki137
The workaround that worked best for me was:
最适合我的解决方法是:
@Suppress("UNCHECKED_CAST")
var pool: Array<T?> = arrayOfNulls<Any?>(initialCapacity) as Array<T?>
回答by CoolMind
I got Type parameter T cannot ba called as function
when tried to return T.
Type parameter T cannot ba called as function
当我试图返回 T 时我得到了。
private fun <T> getData(): T {
return T()
}
Probably this will help:
可能这会有所帮助:
private inline fun <reified T> getData(): T {
return T::class.java.newInstance()
}
But in my case I had to return T(parameter)
, not T()
, so, didn't try. See also How to get class of generic type parameter in Kotlin.
但在我的情况下,我不得不返回T(parameter)
,而不是T()
,所以,没有尝试。另请参阅如何在 Kotlin 中获取泛型类型参数的类。