泛型字节码学习记录

//抛出问题
泛型的类型擦除,Java与Kotlin的泛型都是假泛型,会在运行时被擦除,ArrayList<String>() 在运行时等于 ArrayList<Object>()。
所以像 Gson.fromJson 这样在运行时通过类型来创建对象的操作就必须给它一个实实在在的 Class。
   比如 Gson().fromJson(JsonBean.class),传入的就是一个 Class<JsonBean>,这样才能正确解析;
   此时可能有人要问了:这个 JsonBean 不还是泛型吗?,我可没见过 JsonBeanClass 类,实际传过去的应该是个 Class<Object>。
   其实这就好比把 String 放到 List<Object> 里,声明的是个 Object 类型,但里面放的实际是 String 的对象。
   此处同理,我们是用 JsonBean.class 创建的 Class,里面的存放的是 JsonBean 的类型信息。而 Gson() 是通过 Class 中的类型信息来解析的。
然而当传入的 Class 的泛型有泛型时,比如 Class<List<String>>,问题又出现了,Class 中包含 List 的类型信息,但 List 不是 Class,它可不包含 String 的类型信息。
   作为 Gson 的开发者,别人在编译时写着 Class<List<Integer>> ,但运行时却丢给你一个 Class<List>,又叫你把"3"塞进列表去,那我怎么可能知道你要的类型不是 String 而是 Integer。
   为了解决这问题,需要记住用户传入的所有泛型,可以让用户这样传参数:Gson().fromJson(ArrayList.class ,Integer.class),但它不仅麻烦还不灵活。
   那如何轻便的传来所有泛型信息呢,难道Java官方有提供直接拿到泛型的api吗?

//向下追溯
Class 表示一个类的定义部分、声明部分。但它不是字节码的最底层的,再往下有个 Type 是 Class 的超类,Type 的子类除了 Class 还有 “泛型类型”。
PS: 本文说的“反射类型”是逻辑上的归类,Java 并没有这样的接口
泛型类型中有个 GenericDeclaration(泛型声明许可,属于泛型类型),只有字节码类实现它,才能定义泛型,常用的 Field(成员变量)、Method(方法函数)、Class(类) 正是因为实现了 GenericDeclaration,
才能声明泛型,而 Constructor(构造函数) 没有实现 GenericDeclaration,所以构造函数上就不能声明泛型。
Class 实现了 GenericDeclaration 表示它可以声明泛型,通过 clazz.typeParameters(泛型参数) 就能拿到 TypeVariableImpl(泛型声明),也就是定义Class时泛型是怎么写的,
比如自定义个有泛型的A类:
class A<T : CharSequence>(){}
使用 A::class.java.typeParameters 能得到一个 TypeVariableImpl,它包含泛型的名字:A,约束:上限是 CharSequence。但这个方法拿不到泛型被约束的类型,举个例子,在代码中这样使用A时:
val a = A<String>() //限制 a 的泛型为 String
调用 a.javaClass.typeParameters 拿不到 A<String>() 的使用泛型 Class<String>。
转念一想也应如此,Class 就是个“类型”,能拿到类文件是怎么写的,但拿不到怎么用的。
并且,由于类型擦除的缘故,A<String>() 在编译器的角度来看就是 A<Object>() 这怎么可能拿得到呢。

//寻找线索
Class 的 generic系列(genericSuperclass/genericInterfaces) 就很叼了,generic方法能拿到一个 Type,genericSuperclass 是拿父类的 Type
API细节:若父类没用泛型,实际上拿到的还是 Class,但若父类用了泛型,拿到的就是泛型类。
举个例子:
class B<T : CharSequence>()
class A() : B<CharSequence>()
class B1()
class A1() : B1()
A.class.genericSuperclass 拿到的是 ParameterizedTypeImpl(B<T>),这是泛型实例,属于泛型类。
A1.class.genericSuperclass 拿到的是 Class(B1),不带有泛型。
ParameterizedTypeImpl 可以调用 getActualTypeArguments 方法拿到泛型的实际类型,返回的可能是 TypeVariableImpl(泛型声明)、Class 或 其它泛型类型。
我们的最终目的是拿到使用时的实际泛型,而不是泛型声明,这个方法正好是能返回 泛型类型的。但是仍然不能 A1<String>() 这样写,因为类型擦除会把它擦成 A<Object>()。

//正确的方向
generic系列的神奇之处正在于类在继承时,父类的泛型类型会被锁定成子类的泛型实际类型。
class B<T>()
class A() : B<String>()
//拿到的只是 B 的 TypeVariableImpl(泛型声明),只是 "T" 和 CharSequence。
A::class.java.superclass(拿父类) + clazz.typeParameters(拿泛型声明)
//用generic系列的方法,拿到的就是 A 约束了父类泛型的类型 Class<String>
A::class.java.genericSuperclass(拿父Type) + (type as ParameterizedTypeImpl).getActualTypeArguments(拿泛型类型)
有人可能会问了,直接用 A::class.java.typeParameters 不就能拿到泛型类型了吗,注意,这里的 “A 并没有声明泛型,A 只是约束了泛型的实际类型”,调用 typeParameters 拿不到任何东西。

//最终方案
利用 generic 的继承特性,让用户传入一个继承泛型类的只用于锁定泛型的子类,gson中可以通过generice系列得知泛型的类型,进而解析。
比如 要给 gons 传一个 ArrayList<String>() 就要这么传:
val typeToken = object : TypeToken<ArrayList<String>>() {} //此类只用于锁定泛型类型
val listType = type.type //getType() 的内部包含了泛型解析方法
Gson().fromJson<ArrayList<String>>(jsonStr, listType)
上文中 typeToken 的类型是匿名内部类 TypeToken$1,这个内部类的父类有个泛型 TypeToken<T>。那么我们就可以通过以下代码得知其泛型类型
val typeToken = object : TypeToken<ArrayList<String>>() {}
val genericSuperclass = typeToken.javaClass.genericSuperclass //得到泛型被约束的父类
val actualTypeArguments = (genericSuperclass as ParameterizedType).actualTypeArguments //在下一行打断点,可以看到内部的泛型约束信息。

//泛型类型简述
GenericDeclaration:泛型声明许可,上文已经有描述,只有字节码类实现它,才能定义泛型,常用的 Field(成员变量)、Method(方法函数)、Class(类) 正是因为实现了 GenericDeclaration,才能声明泛型。
   而 Constructor(构造函数) 没有实现 GenericDeclaration,所以构造函数上就不能声明泛型。注意,是声明泛型,不是使用泛型,使用泛型不需要实现此接口,所以可以在构造方法中使用泛型。
TypeVariable:泛型声明信息,上文已经有描述,并不包含约束后泛型的信息,泛型声明处怎么写的,就包含什么信息。
ParameterizedType:泛型实例,是最简单用法的泛型,包含着其它泛型类型,<T>、<E>
   actualTypeArguments // 返回泛型类型数组,包含了泛型约束信息。
   rawType // 返回泛型外层类的信息,是普通的 Class 类型。对于 class A <List<String>>,泛型 List 的外层是 A、 泛型 String 的外层是 List
   ownerType // 返回泛型所在的类的外部类,当没有外部类时返回空。
WildcardType:泛型表达式,使用了 extends/super 表达式的泛型 <T extends CharSequence>
   upperBounds // 返回最父层约束
   lowerBounds // 返回最子层约束
GenericArrayType:网上对它的解释是泛型数组,但是我并没有试出来,约束泛型为数组后,拿到的是 Class 类型
   坑:泛型中不包含数组类型,数组类型属于Class
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值