ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
通常情况下使用泛型我们并不在意它的类型是否是类型擦除,但是**在有些场景,我们却需要知道运行时泛型参数的类型,比如序列化/反序列化的时候。这时候我们应该怎么办**?通过前面的学习相信你对Java和Kotlin的泛型实现原理已经有了一定的了解,*既然编译后会擦除泛型参数类型,那么我们是不是可以**主动指定参数类型来达到运行时获取泛型参数类型的效果呢***?我们试着对上面的例子的Plate进行一下改造: open class Plate<T>(val t : T, val clazz: Class<T>) { fun getType() { println(clazz) } } val applePlate = Plate(Apple(1.0), Apple::class.java) applePlate.getType() //结果 class Apple 使用这种方式确实可以达到运行时获取泛型类型参数的效果。但是**这种方式也有限制,比如我们就无法获取一个泛型的类型**,比如: val listType = ArrayList<String>::class.java //不被允许 val mapType = Map<String, String>::class.java //不被允许 那么,还有没有另外的方式能获取各种类型的信息呢?有,那就是**利用匿名内部类**。我们来看下面的一个例子: val list1 = ArrayList<String>() val list2 = object : ArrayList<String>(){} //匿名内部类 println(list1.javaClass.genericSuperclass) println(list2.javaClass.genericSuperclass) //结果: java.util.AbstractList<E> java.util.ArrayList<java.lang.String> 不可思议,第2种方式竟然能在运行时知道这个list是一个什么样的类型。心细的读者应该发现,list2声明的其实是一个匿名内部类。关于如何在Kotlin中用object来声明一个匿名内部类的相关知识可以回顾一下前面相应内容。那么,**为什么使用匿名内部类的这种方式能够在运行时获取泛型参数的类型呢?其实泛型类型擦除并不是真的将全部的类型信息都擦除,还是会将类型信息放在对应class的常量池中的**。 Java将泛型信息存储在哪里? 可以参考以下网页:[Where are generic types stored in java class files?](https://stackoverflow.com/questions/937933/where-are-generic-types-stored-in-java-class-files/937999#937999) 所以,**既然还存储着相应的类型信息,那么我们就能通过相应的方式来获取这个类型信息。使用匿名内部类我们就可以实现这种需求**。我们着手来设计一个能获取所有类型信息的泛型类: import java.lang.reflect.ParameterizedType import java.lang.reflect.Type open class GenericsToken<T> { // var type: Type = Any::class.java init { val superClass = this.javaClass.genericSuperclass type = (superClass as ParameterizedType).getActualTypeArguments()[0] } } fun main(args: Array<String>) { val gt = object : GenericsToken<Map<String, String>>(){} //使用object创建一个匿名内部类 println(gt.type) } //结果 java.util.Map<java.lang.String, ? extends java.lang.String> **匿名内部类在初始化的时候就会绑定父类或父接口的相应信息,这样就能通过获取父类或父接口的泛型类型信息来实现我们的需求**。你可以利用这样一个类来获取任何泛型的类型,我们常用的Gson也是使用了相同的设计。 Gson的TypeToken实现参考以下网址:https://github.com/google/gson/blob/master/gson/src/main/java/com/google/gson/reflect/TypeToken.java 比如,我们在Kotlin中可以这样使用Gson来进行泛型类的反序列化: val json = ... val rType = object : TypeToken<List<String>>() {}.type val stringList = Gson().fromJson<List<String>>(json, rType) 其实,**在Kotlin中除了用这种方式来获取泛型参数类型以外,还有另外一种方式,那就是内联函数**。