运行时泛型获取

1.背景

今天在使用JSON.parseObject方法时,发现了有下面这个方法

    /**
     * <pre>
     * String jsonStr = "[{\"id\":1001,\"name\":\"Jobs\"}]";
     * List&lt;Model&gt; models = JSON.parseObject(jsonStr, new TypeReference&lt;List&lt;Model&gt;&gt;() {});
     * </pre>
     * @param text json string
     * @param type type refernce
     * @param features
     * @return
     */
public static <T> T parseObject(String text, TypeReference<T> type, Feature... features) {
        return (T) parseObject(text, type.type, ParserConfig.global, DEFAULT_PARSER_FEATURE, features);
    }

就是可以利用Typereference来保证返回参数时保留泛型,这样的话可以避免很多强转,看了一下这个方法的注解,意思是让我new TypeReference<List<Model>>() {},在TypeReference的泛型中传入你想返回的泛型即可,于是我尝试自己写了一下,却报错了,代码如下

JSON.parseObject(JSON.toJSONString(ressult), new TypeReference<Res<CameraInfo>>());

我仔细看了这个方法的注解,发现其实不是直接new TypeReference<Res<CameraInfo>>(),而是new一个他的匿名内部类,我就在想为什么非要创建一个他的匿名内部类呢?而且这个匿名内部类里面什么也没有写,于是我继续去看了TypeReference的源码,其获取泛型的方法如下

    /**
     * Constructs a new type literal. Derives represented class from type
     * parameter.
     *
     * <p>Clients create an empty anonymous subclass. Doing so embeds the type
     * parameter in the anonymous class's type hierarchy so we can reconstitute it
     * at runtime despite erasure.
     * <pre>
     *
     * 构造一个新类型文字。 从类型参数派生表示类。  
	 * 客户端创建一个空的匿名子类。 这样做将在匿名类的类型层次结构中嵌入类型参数,这样我们就可以在运行时重新构造它,尽管已经擦除。
     * </pre>
     */
    protected TypeReference(){
        //获取父类的带泛型的class
        Type superClass = getClass().getGenericSuperclass();

        //获取父类的泛型
        Type type = ((ParameterizedType) superClass).getActualTypeArguments()[0];

        //缓存泛型并返回
        Type cachedType = classTypeCache.get(type);
        if (cachedType == null) {
            classTypeCache.putIfAbsent(type, type);
            cachedType = classTypeCache.get(type);
        }

        this.type = cachedType;
    }

可以看到在方法的注释上,也注明了使用方法,即创建一个匿名子类来获取其泛型,而TypeReference这个类的构造方法也是protected修饰的,这样就保证了我们不能直接创建这个class来进行使用。为什么非要子类呢?直接使用这个类不行吗?于是我到网上去详细了解了一下泛型以及泛型擦除等特性,找到以下两篇能很好解释的文章。

2.了解泛型的特性

Java 的泛型擦除和运行时泛型信息获取 这篇文章,是直接讲解Java 的泛型擦除和运行时泛型信息获取的,相当于直接解释了,为什么必须创建匿名子类才能获取到泛型内容,但是里面只是简单的提到了*简而言之,Java 的泛型擦除是有范围的,即类定义中的泛型是不会被擦除的。*但是并没有讲清楚擦除的范围是多大,什么时候泛型不会被擦除,以及为什么在这种情况下泛型不会被擦除。于是我继续详细了解泛型的特性

java泛型(二)、泛型的内部原理:类型擦除以及类型擦除带来的问题 这篇文章是对泛型的特性的详细介绍,讲的非常详细。而在其三、类型擦除引起的问题及解决方法中的3、类型擦除与多态的冲突和解决方法中我找到了我想要的东西,这一节十分详细的讲解了为什么在子类继承时,泛型不会被擦除–即利用桥方法,将父类的方法与子类的方法做一个转换。

3.疑问

如果是子类在继承时,泛型不会被擦除,那么为什么非要匿名子类?如果建一个类继承泛型类放在包中,平时使用时就使用这个子类可以吗?答案是不行的,或者说是不方便的。如果是想写一个类然后所有地方通用是不可行的,代码如下

public class SubTypeReference<T> extends TypeReference<T>{
    //*********
}

这种情况下,子类的泛型类型还是会被擦除,即类型还是Object,那么不能获取到泛型的类型,则达不到目的。当然了,可以重新很多个通过的子类,例如

public class SubTypeReference<String> extends TypeReference<String>{
    //*********
}

这样的话,后面返回的泛型为String类型的,则可以通用这个子类,不过感觉这样意义不大,因为这样的话要重新很多个类放在包里,而且很多情况下,需要转换的泛型类型都更复杂,因此写匿名子类是最合适的,也是最方便的。

4.总结:

  1. java的泛型并非真正的泛型,只是会在编译阶段判断你的代码是否符合泛型的规定,如果不符合泛型要求,则会报错。而在编译之后,运行时,泛型的类型都是Object或者指定的泛型继承类,获取的时候再通过强转返回
  2. 通过子类继承的方式可以获取到泛型的内容。是因为父类虽然擦除了泛型的具体类型,但是子类在继承之后生成的字节码,是通过桥方法转换,这样就保留了我们指定的类型
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值