在运行时获取泛型的类型

来源:互联网 发布:网络销售招聘58同城 编辑:程序博客网 时间:2024/05/21 15:02

Java 1.5在加入泛型支持时,为了保持兼容,采用的是擦除法实现,泛型的定义只在编译的时候有效,编译之后是没有保留泛型的类型信息的。所以,通常是无法在运行时获得泛型的类型活实例化泛型类的,如下面的代码都无法通过编译:

 

public class Test<T> {
    public Test(){
        T t = new T();    // error
        Class<T> clazz = T.class;   //error
        T[] ts = new T[10];   //error
    }
}

然而,擦除法的实现存在一些特列,在这些特例情况下,Java会记录泛型的类型信息,并且可以通过反射的Api来获取。比如在各种持久化框架中广泛使用的BaseDao泛型类的写法,就利用一个泛型继承的特例: 

public class BaseDAO<U> {
    private Class<U> entityClass;

    protected BaseDAO() {
        // 获取泛型类型
        Type type = getClass().getGenericSuperclass();  
        Type[] trueType = ((ParameterizedType) type).getActualTypeArguments();
        this.entityClass = (Class<U>) trueType[0];
    }
       
    public U getById(Serializable id){
        PersistenceManager pm = getPersistenceManager();
        pm.setDetachAllOnCommit(true);
        U object;
        try{
            object = pm.getObjectById(entityClass, id);
        }catch (JDOObjectNotFoundException e) {
            object = null;
        }finally{
            pm.close();
        }
        return object;
    }
}

public class TagDAO extends BaseDAO<Tag>{
}

代码的关键是getGenericSuperclass方法,这个方法返回的类型为ParameterizedType,里面可以通过getActualTypeArguments来获取泛型的真正类型。

Jackson json中反序列化时,可以通过TypeReference传递容器中的泛型类型,也是利用了泛型继承的特例:

Map<Integer, ApkBean> map = mapper.readValue(jsonData, new TypeReference<Map<Integer, ApkBean>>(){});

TypeReference是一个泛型抽象类,在readValue的第二个方法中,传入了TypeReference的一个匿名子类实例,由此带入了Map的泛型信息。

除了泛型继承这种情况之外,还有另外两个特例,也可以获取ParameterizedType类型,继而获取到泛型的类型。一个是类的field可以通过getGenericType来获取:

public class Test {

    private Map<String, Number> map;

    public static void main(String[] args) throws NoSuchFieldException {
        Class<?> clazz = Test.class;
        Field field = clazz.getDeclaredField("map");
        //取得泛型类型
        Type type = field.getGenericType();
        ParameterizedType ptype = (ParameterizedType)type;
        System.out.println(ptype.getActualTypeArguments()[0]);
        System.out.println(ptype.getActualTypeArguments()[1]);
    }

}

另外就是方法的泛型参数可以通过getGenericParameterTypes来获取:

public class Test {

    public static void main(String[] args) throws NoSuchMethodException {
        Class<?> clazz = Test.class;
        Method method = clazz.getDeclaredMethod("getGenericSample", Collection.class);
        //取得泛型类型参数集
        Type[] type = method.getGenericParameterTypes();
        ParameterizedType ptype = (ParameterizedType)type[0];
        type = ptype.getActualTypeArguments();
        System.out.println(type[0]);
    }

    public void getGenericSample(Collection<Number> collection){

    }
}

这两种特例的实用价值比较小,实际的代码hack中也比较少见。

0 0
原创粉丝点击