获取泛型类运行时刻实际成员类型

来源:互联网 发布:mac os vmware 镜像 编辑:程序博客网 时间:2024/05/01 06:17

  新的工作开始了,自己也做了一个规划,所以从现在开始要坚持写博客,以前也写,不过都是在QQ空间上,这次动真格的,好好的把自己平时遇到的、见过的、有趣的东西记下来,我相信,自己的规划能让自己活出个精彩。


由于编译擦除,运行时刻无法获得成员的真实类型

  现在的工作主要是给移动端提供访问服务器的接口,那么多多少少的都要涉猎些和安卓苹果开发有关的东西,在准备给安卓APP提供接口的时候,我看到了这么一个东西:Gson,这个包是Google提供的,主要用来做Json和Java对象间的转化,主要是对包含泛型类的序列化和反序列化提供了非常好的支持,那么这里就涉及到一个问题:
- 因为Java的泛型机制,运行期间泛型参数类型会被擦除,那么当我们真的需要知道泛型参数类型的时候,该怎么做?


  ok,先上一个例子,看看问题所在:

package com.bubbling;   //我自己的冒泡工作室,BubblingStudioimport java.lang.reflect.Field;public class Test<T> {    T para;    public static void main(String[] args) {        Test<String> t = new Test<>();        Field[] fs = t.getClass().getDeclaredFields();        for(Field f : fs) {            System.out.println(f.getType());        }    }}

  代码非常简单,给结果看一下,一目了然:
这里写图片描述
  因为编译擦除,很清楚的看到运行时刻属性para的类型是Object,那么如果在运行时刻要得到成员类型就变得比较麻烦,接下来介绍两种方法,原理均来自Gson,但是我并不想把Gson中的东西拷贝出来,我会用Java的方式来解读它们。


Class类型作为成员保存类型信息

  第一种是常见的做法,就是在泛型类的构造函数中,要求显式的传入泛型类的Class类型作为其私有属性,由它来保存泛型类的类型信息。如下:

package com.bubbling;import java.lang.reflect.Field;public class Test<T> {    Class<T> para;    public Test(Class<T> type) {        this.para = type;    }    public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {        Test<String> t = new Test<>(String.class);        Field[] fs = t.getClass().getDeclaredFields();        for(Field f : fs) {            System.out.println(f.get(t));        }    }}

  运行结果如下:
这里写图片描述
  通过Class成功的保存了泛型类成员的实际类型,但是这个办法有一个不好的地方,就是每次创建实例的时候,都需要显示的传入一个Class,为了解决这个问题,还有一个更高端的方法。


利用Class字节码常量池中的signature签名

先上代码:

package com.bubbling;import java.lang.reflect.ParameterizedType;import java.lang.reflect.Type;public class Test<T> {    Class<T> para;    @SuppressWarnings("unchecked")    public Test() {        this.para = (Class<T>)getClass();    }    public static void main(String[] args) {        Test<String> t = new Test<String>() {};        Type myType = t.getClass().getGenericSuperclass();        Type[] types = ((ParameterizedType)myType).getActualTypeArguments();        for(Type type : types) {            System.out.println(type);        }    }}

  运行结果如下:
这里写图片描述
分析一下原理:
  成功的关键点就在匿名子类,匿名子类将泛型类作为参数传入构造函数,然后通过getClass()方法获得这个子类的Class类型。
  而问题的关键点在于虽然我们得到了匿名子类的Class类型,但是我们并没有直接把泛型参数的Class类型传入,那么是怎么获得泛型参数类型的呢?
  原因是Java的Class字节码中存储了泛型参数的信息,虽然Java泛型机制会擦除类型信息,但是class文件中依然保存了泛型相关的信息,这些信息存在于字节码常量池中,并且所处位置被signature签名标识,通过这个签名字段指明泛型信息存储地址。
  而JDK里也提供了若干方法读取这些泛型信息,通过反射可以得到具体的泛型参数类型。


泛型相关的反射API介绍

getGenericSuperclass()
该方法会返回一个Type类型的对象,如果该Class对象带有泛型信息,实际Type类型为ParameterizedType,如果是Object、接口或者原始类型则返回null,对于数组class来说则返回Object.class。
Type接口
JDK1.5之后引入了泛型概念,所以Java中所有的Class都实现了Type接口,Parameterized继承自Type,并且所有包含泛型的Class类都会实现该接口。
getActualTypeArguments()
该方法返回表示此类型实际类型参数的Type对象的数组,调用此方法的时候,应该先判断该类是否支持泛型。

2017年3月17日 胡楠 冒泡工作室:BubblingStudio

0 0
原创粉丝点击