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

     2023-02-26     271

关键词:

【中文标题】Kotlin 泛型 Array<T> 导致“不能将 T 用作具体类型参数。请改用类”但 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 【发布时间】:2022-01-17 02:36:17 【问题描述】:

我有一个接口,其中包含 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 开发人员偏爱数组。是这样吗,如果是,为什么?

【问题讨论】:

【参考方案1】:

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

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

reified 类型参数意味着你可以在编译时访问T 的类,并且可以像T::class 一样访问它。您可以在Kotlin reference 中阅读有关reified 类型参数的更多信息。由于Array&lt;T&gt; 编译为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&lt;Nothing&gt;。 kotlin 类型 Nothingthrow 关键字的返回类型,并且是永远不存在的值 (reference)。如果一个方法返回Nothing,就相当于在那个地方抛出异常。所以我们可以在这里安全地使用Nothing,因为每次我们调用EmptyList.get(),编译器都知道这会返回一个异常。


额外问题:

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

【讨论】:

【参考方案2】:

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

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

只能创建像Array&lt;String&gt;Array&lt;Int&gt; 这样的具体数组,但不能创建Array&lt;T&gt; 类型的数组。

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

【讨论】:

【参考方案3】:

最适合我的解决方法是:

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

【讨论】:

【参考方案4】:

我在尝试返回 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()

但就我而言,我必须返回T(parameter),而不是T(),所以,没有尝试。另见How to get class of generic type parameter in Kotlin。

【讨论】:

【参考方案5】:

我在上面的解决方案中遇到了一些问题。这是我使用来自kotlin-reflect 的typeOf 得出的结论:

@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得到的泛型类,第二个参数是数组的长度。

【讨论】:

kotlin泛型

...类型参数化,可以用在类,接口,方法上。与Java一样,Kotlin也提供泛型,为类型安全提供保证,消除类型强转的烦恼。声明一个泛型类:classBox<T>(t:T)varvalue=t创建类的实例时我们需要指定类型参数:valbox:Box<Int>=Box<Int>(... 查看详情

为啥 Kotlin Array<T> 不实现 Iterable<T>

】为啥KotlinArray<T>不实现Iterable<T>【英文标题】:Whydoesn\'tKotlinArray<T>implementIterable<T>为什么KotlinArray<T>不实现Iterable<T>【发布时间】:2016-06-2614:53:48【问题描述】:为什么Kotlin中的Array&lt;T&gt;类不实... 查看详情

kotlin数组和集合

参考技术Akotlin为数组增加了Array类,为元素是基本类型的数组增加了XxxArray类(其中Xxx可以是Byte、Short、Int等基本类型)kotlin的集合体系抛弃了Java集合体系中的Queue集合,但增加了可变集合和不可变集合的概念,与java集合类似的... 查看详情

泛型类型 'Array<T>' 需要 1 个类型参数。 - 角2

】泛型类型\\\'Array<T>\\\'需要1个类型参数。-角2【英文标题】:Generictype\'Array<T>\'requires1typeargument(s).-Angular2泛型类型\'Array<T>\'需要1个类型参数。-角2【发布时间】:2017-12-0511:38:54【问题描述】:我一直在尝试使用Angul... 查看详情

kotlin基础接口泛型

接口定义:Interface接口名//各种属性或方法定义//接口和泛型interfaceIBaseDao<T>valcity:Stringget()="北京"vargrade:Intfun<T>add(t:T)fundel(id:Int)funupdate(id:Int,stu:Student)funselect()//实现接口open 查看详情

149ts泛型

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。//泛型函数的类型与非泛型函数的类型没什么不同,只是有一个类型参数在最前面,像函数声明一样.functioncreat... 查看详情

java泛型数组

publicclassGenericArrayWithTypeToken<T>{privateT[]array;publicGenericArrayWithTypeToken(Class<T>type,intsz){array=(T[])Array.newInstance(type,sz);//array=(T[])newObject[sz];会出现类型转换异常}publi 查看详情

java中定义泛型<t>时,怎么获得泛型的类型

T.getClass()或者T.class都是非法的,因为T是泛型变量。由于一个类的类型是什么是在编译期处理的,故不能在运行时直接在Base里得到T的实际类型。有一种变通的实现方式:importjava.lang.reflect.Array;importjava.lang.reflect.ParameterizedType;impor... 查看详情

如何从泛型observable订阅publishrelay?(代码片段)

Kotlin代码:fun<T>Observable<T>.circuitBreaker():Observable<T>valrelay=PublishRelay.create<T>()this.subscribe(relay)returnrelay.toFlowable(BackpressureStrategy.LATEST).toObservable()上面是一个Kotlin代码,我试图将其转换为Swift。但是,我正面临一个... 查看详情

kotlin泛型①(泛型类|泛型参数|泛型函数|多泛型参数|泛型类型约束)(代码片段)

文章目录一、泛型类二、泛型参数三、泛型函数四、多泛型参数五、泛型类型约束一、泛型类定义一个泛型类,将泛型参数T放在尖括号<T>中,该泛型参数放在类名后,主构造函数之前,该泛型参数T是类型占位符,在该泛型类类中... 查看详情

kotlin入门到精通(实战)数组的创建和操作详解(代码片段)

<<<返回专栏总目录本章目录数组的创建方式方式1方式2方式3方式4创建多维数组数组的操作map操作flatMap操作fold操作associate操作associateBy操作distinct操作distinctBy操作数组的创建方式方式1通过Array类来创建数组。Array类的声明... 查看详情

kotlin基础知识九:generics

...s)替换形参(typeparameters)。一般来说,实参类型可以由kotlin编译器推断而出,例如:可以省去显式的类型声明(explicittypespecification)。如果创建的是一个空的list,就无法推断出实参类型,因此需要显式的指定它:与Java不同的... 查看详情

kotlin泛型总结★(泛型类|泛型参数|泛型函数|多泛型参数|泛型类型约束|可变参数结合泛型|out协变|in逆变|reified检查泛型参数类型)(代码片段)

文章目录一、泛型类二、泛型参数三、泛型函数四、多泛型参数五、泛型类型约束六、可变参数vararg关键字与泛型结合使用七、使用[]运算符获取指定可变参数对象八、泛型out协变九、泛型in逆变十、泛型invariant不变十一、泛型... 查看详情

如何键入使用 Array.push() 的泛型函​​数?

】如何键入使用Array.push()的泛型函​​数?【英文标题】:HowtotypeagenericfunctionthatusesArray.push()?【发布时间】:2020-11-2716:14:44【问题描述】:在研究泛型时:functionnewArr<T>(arr:T[],itemToAdd:string):T[]returnarr.push(itemToAdd);constarr:string[... 查看详情

kotlin泛型②(可变参数vararg关键字与泛型结合使用|使用[]运算符获取指定可变参数对象)(代码片段)

文章目录一、可变参数vararg关键字与泛型结合使用二、使用[]运算符获取指定可变参数对象一、可变参数vararg关键字与泛型结合使用如果泛型类型T的参数是vararg可变参数,则在接收可变参数时,需要使用Array<outT>类型的变量进... 查看详情

scala的泛型(代码片段)

 类比java中的泛型: 上界(协变)、下界(逆变)scala中泛型采用中括号声明valarray=Array[Int](1,2,3,4)array(3)//声明什么类型就返回什么类型//test[String]()deftest[T](t:T)=//声明什么类型就返回什么类型泛型上界test[User2](newUser3)deftes... 查看详情

kotlin/java之常用“集合型”数据类型

******先说说数组Array和ArrayList ****** <数组Array>整型数组初始化:valpoint=intArrayOf(0,0) //实为IntArray,对应Java类型int[]其他还有:charArrayOf、byteArrayOf、longArrayOf、floatArrayOf、booleanArrayOf等,详见Library.kt泛型Array... 查看详情

在 kotlin 中创建泛型类的新实例的正确方法是啥?

】在kotlin中创建泛型类的新实例的正确方法是啥?【英文标题】:Whatistheproperwaytocreatenewinstanceofgenericclassinkotlin?在kotlin中创建泛型类的新实例的正确方法是什么?【发布时间】:2014-11-1810:29:55【问题描述】:我使用以下初始化:... 查看详情