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的实际类型,所以解决方法也相同

我们知道,泛型是 编译时写入,使用时不写入,即是说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))

 

除了自己手动拼装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=${m1}毫秒 m2=${m2}毫秒")
}

打印结果:耗时:m1=5毫秒 m2=0毫秒

猜你喜欢

转载自blog.csdn.net/jingzz1/article/details/107861870