Only classes are allowed on the left hand side of a class literal

kotlin中,如果我们希望获取到某个类或属性的类型,一般会用 ::class.java 或者 .javaClass,比如我们希望获取String的type,可以写成String::class.java 这样写的合法的,因为编译器知道String的类型

但当一个对象有多个泛型嵌套时,比如:List<String>::class.java ,这时编译器就会提示:Only classes are allowed on the left hand side of a class literal,编译不通过

java也一样,List<String>.class也是不合法的,会报错:Cannot select from parameterized type ,这其实都是同一种错误,即编译器无法识别class的实际类型,所以解决方法也相同

我们知道,类型只有编译时写入,使用时不写入,即是说class List<T>(){}在编译时只是写入List的类型,并没有写入T的类型,所以我们直接写List<String>::class.java,编译器根本无法获取到对象的实际类型(编译器获取到的只是Class<List<*>>,而不是我们希望的Class<List<String>>)如果我们希望表示泛型嵌套时的具体类型,需要使用 ParameterizedType(参数化类型) 来表示,ParameterizedType是Type的子类,表示一个有参数的类型的具体类型,比如Map<String,Int>,List<Int>等嵌套泛型的具体类型

ParameterizedType方法介绍

ParameterizedType有三个方法需要实现:

getRawType() :返回最外层的类型,比如Map<K,V>中的Map

getActualTypeArguments() :返回<>中的类型,比如Map<K,V>中的K和V,因为可能有多个,所以以数组返回

getOwnerType():返回这个类型的所有者类型,如果这个类是个顶层类,那么返回null,如果是个内部类,那么就返回这个类的外层,比如 View.OnClickListener的View

如果我们希望获取到List<String>、List<List<String>>、Map<Int,String>、Map<String,List<String>>的实际类型,那需要构建一个ParameterizedType

ParameterizedType我们一般会用两种方法去获取,一种是自己构建,另一种是通过反射获取

自己构建ParameterizedType,只需要重写ParameterizedType三个方法即可:

fun getType(raw: Class<*>, vararg args: Type) = object : ParameterizedType {
    override fun getRawType(): Type = raw
    override fun getActualTypeArguments(): Array<out Type> = args
    override fun getOwnerType(): Type? = null
}

 使用时,传入具体类型,拼装成ParameterizedType

//List<String>的type为
 val type = getType(List::class.java,String::class.java)

//List<List<String>>的type为
 val type = getType(List::class.java,getType(List::class.java,String::class.java))

//Map<Int,String>的type为
val type = getType(List::class.java,Int::class.java,String::class.java)

//Map<String,List<String>>的类型为
val type = getType(Map::class.java,String::class.java, getType(List::class.java,String::class.java))

※koltin在使用基本数据类型时,会根据需要自动封箱(如Int在jvm里会自动转成int或Integer),而泛型要求使用的必需是Object类型,因此在使用ParameterizedType传入基本类型时,需要使用它的包装类,比如获取MutableList<Int>的type需要这样写:

//MutableList<Int>的type
val type =  getType(MutableList::class.java,Int::class.javaObjectType)

除了自己手动拼装ParameterizedType外,通过反射也可以拿到参数化类型

先写一个通过泛型获取实际type的类:TypeToken

abstract class TypeToken<T>{
    protected val type: Type  = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0]
    companion object{
        inline fun <reified T>getType() = object :TypeToken<T>(){}.type
    }
}

然后通过TypeToken传入泛型就可以获取到ParameterizedType了

//获取Map<String,List<String>>的实际类型 
 val type = TypeToken.getType<Map<String,List<String>>>()

可以看出这种方法比拼装简单,但由于使用了反射,性能却不如拼装

fun main(args: Array<String>) {
    val m1 = measureTimeMillis {
        val type = TypeToken.getType<Map<String,List<String>>>()
    }
    val m2 = measureTimeMillis {
        val type = getType(Map::class.java,String::class.java, getType(List::class.java,String::class.java))
    }
     println("耗时:反射耗时=${m1}毫秒 拼装耗时=${m2}毫秒")
}

打印结果:耗时:反射耗时=5毫秒 拼装耗时=0毫秒

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值