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毫秒