添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
Java中的泛型有着很重要的作用,它能够让我们的数据容器类型安全,避免发生转换异常。不过Java中的泛型也为人诟病,它会在编译中被全部转换成Object对象,也就是泛型擦除,这造成了诸多不便,除非你能获取泛型的一个实例,否则我们无法直接获取泛型的实际类型。不过JDK依然提供了一个技巧让我们可以获得泛型的具体类型。

Java中的泛型有着很重要的作用,它能够让我们的数据容器类型安全,避免发生转换异常。不过Java中的泛型也为人诟病,它会在编译中被全部转换成 Object 对象,也就是泛型擦除,这造成了诸多不便,除非你能获取泛型的一个实例,否则我们无法直接获取泛型的实际类型。不过JDK依然提供了一个技巧让我们可以获得泛型的具体类型。

虽然泛型会在字节码编译过程中被擦除,但是 Class 对象会通过 java.lang.reflect.Type 记录其实现的接口和继承的父类信息。我们以 ArrayList<E> 为例:

        ArrayList<String> strings = new ArrayList<>();
        Type genericSuperclass = strings.getClass().getGenericSuperclass();
        // genericInterfaces = java.util.AbstractList<E>
        System.out.println("genericSuperclass = " + genericSuperclass);
 

虽然我们成功打印出来了泛型的占位符E,但是这并不是我们想要的。我们希望能够获取E的具体类型,也就是String

让我们回到java.lang.reflect.Type

Type的实现类型

通过上图可以知道Type有四种类型:

  • GenericArrayType 用来描述一个参数泛型化的数组。

  • WildcardType 用来描述通配符?相关的泛型,包含的?、下界通配符? super E 、上界通配符? extend E

  • Class<T>  用来描述类的Class对象。

  • ParameterizedType 用来描述参数化类型。

  • 我们再来试一试:

    ArrayList<String> strings = new ArrayList<>();
    Type genericSuperclass = strings.getClass().getGenericSuperclass();
    System.out.println( genericSuperclass instanceof ParameterizedType); // true
    System.out.println( genericSuperclass instanceof Class); // false
    System.out.println( genericSuperclass instanceof WildcardType); // false
    System.out.println( genericSuperclass instanceof GenericArrayType); // false
     

    我们来看看参数化类型能不能获取到具体的类型:

            ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            // [E]
            System.out.println("actualTypeArguments = " + Arrays.toString(actualTypeArguments));
     

    ParameterizedType可以帮助我们获取参数类型,可惜依然是E。两种方法为什么都只能获取一个泛型占位符呢?

    public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    // 省略
     

    这是因为ArrayList实例化时只指定了自己的泛型类型而没有指定父类AbstractList的具体泛型,所以获取到的就是占位符E。我们先来看一个抽象类例子:

      public abstract  class SuperClass<E> {
            private E e;
            protected SuperClass(E e) {
                this.e = e;
            public E get() {
                return this.e;
     

    构建匿名子类实现:

    // 实现
     SuperClass<String> superClassInstance = new SuperClass<String>("试一试") {
    Type genericSuperclass1 = superClassInstance.getClass().getGenericSuperclass();
    //SuperClass<java.lang.String>
    System.out.println(genericSuperclass1);
     

    我们通过大括号{}就可以重写实现父类的方法并指定父类的泛型具体类型。我们可以借助这一特性来获取父类的具体泛型类型,我们还拿ArrayList来试试:

    ArrayList<String> strings = new ArrayList<String>(){};
    Type genericSuperclass = strings.getClass().getGenericSuperclass();
    // genericSuperclass = java.util.ArrayList<java.lang.String>
    System.out.println("genericSuperclass = " + genericSuperclass);
     

    证明了我们的猜想是对的。那么问题来了如何封装一个工具类?

    封装工具类

    我们可以借助于抽象类来定义一个获取java.lang.reflect.ParameterizedType的工具类。好在Spring框架中已经提供了一个很好用的实现:

    public abstract class ParameterizedTypeReference<T> {
        private final Type type;
        protected ParameterizedTypeReference() {
            Class<?> parameterizedTypeReferenceSubclass = findParameterizedTypeReferenceSubclass(this.getClass());
            // 获取父类的泛型类 ParameterizedTypeReference<具体类型>
            Type type = parameterizedTypeReferenceSubclass.getGenericSuperclass();
            // 必须是 ParameterizedType
            Assert.isInstanceOf(ParameterizedType.class, type, "Type must be a parameterized type");
            ParameterizedType parameterizedType = (ParameterizedType)type;
            // 获取泛型的具体类型  这里是单泛型
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            Assert.isTrue(actualTypeArguments.length == 1, "Number of type arguments must be 1");    // 设置结果
            this.type = actualTypeArguments[0];
        // 这个方法开放出去用来调用 来获取泛型的具体Class类型 
        public Type getType() {
            return this.type;
        private static Class<?> findParameterizedTypeReferenceSubclass(Class<?> child) {
            Class<?> parent = child.getSuperclass();
            // 如果父类是Object 就没戏了
            if (Object.class == parent) {
                throw new IllegalStateException("Expected ParameterizedTypeReference superclass");
            } else {
                // 如果父类是工具类本身 就返回否则就递归 直到获取到ParameterizedTypeReference
                return ParameterizedTypeReference.class == parent ? child : findParameterizedTypeReferenceSubclass(parent);
      

    其实 Jackson Gson都有类似的实现。

    所以今天你又学了一招,而且这一招相当的有创意。这一招在封装一些通用类库的时候非常有用,比如反序列化工具类。看完了别忘关注码农小胖哥并一键四连哦。

    后端Java开发如何防御XSS攻击

    2021-06-30

    Windows 11正式发布,Win10用户可免费升级

    2021-06-25

    文章来源: felord.blog.csdn.net,作者:码农小胖哥,版权归原作者所有,如需转载,请联系作者。

    原文链接:felord.blog.csdn.net/article/details/118384336