Java 泛型机制(类型擦除),附加签名信息中获取泛型类型

类型擦除机制(编译时擦除为Object)

  • 使用泛型的时候加上的类型参数,会在编译的时候去掉(在生成的Java字节码中是不包含泛型中的类型信息的)。这个过程就称为类型擦除。

优势

  • Java 1.5 才开始引入泛型,兼容性问题,兼容之前的版本

缺陷

  • 基本类型无法作为泛型实参(因为编译时被擦除为Object类型),只能用包装类型,装箱开箱有开销
  • 泛型类型无法当做真实的类型使用,因为编译后的泛型为Object类型
  • 泛型类型无法用方法重载,因为编译后都是List list
    public void print(List<Integer> list){
    
     }
    public void print(List<String> list){
    
     }
  • 静态方法无法引用类泛型参数(可以给静态方法单独加上泛型参数),因为只有类实例化的时候才会知道泛型参数,而静态方法不需要持有类的实例
  • 类型强转时的开销(Object强转到对应的类型)

附加的签名信息特定场景下反射可以获取

  • 因为类型擦除,创建子类才可以获取到父类的泛型信息
class SuperClass<T> {
    
    

}

class SubClass extends SuperClass<String> {
    
    
    public List<Map<String, String>> getValue() {
    
    
        return null;
    }
}

public class ATest {
    
    
    @Test
    public void test3() {
    
    
        Class<? extends SuperClass> aClass1 = new SuperClass<Integer>().getClass();
        Class<? extends SuperClass<Integer>> aClass2 = new SuperClass<Integer>() {
    
    
        }.getClass();
        Class<SubClass> aClass3 = SubClass.class;
        //
        printType(aClass3);
        printType(aClass2);
        printType(aClass1);//报错
    }

    void printType(Class<? extends SuperClass> aClass) {
    
    
        ParameterizedType genericSuperclass = (ParameterizedType) aClass.getGenericSuperclass();
        System.out.println(genericSuperclass.getActualTypeArguments()[0]);
    }
}

/*
class java.lang.String
class java.lang.Integer
java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
*/
  • 获取方法中的泛型信息
class SuperClass<T> {
    
    

}

class SubClass extends SuperClass<String> {
    
    
    public List<Map<String, String>> getValue() {
    
    
        return null;
    }
}

public class ATest {
    
    
    @Test
    public void test4() throws NoSuchMethodException {
    
    
        Class<SubClass> aClass3 = SubClass.class;
        ParameterizedType genericSuperclass = (ParameterizedType)aClass3
        .getMethod("getValue")
        .getGenericReturnType();
        System.out.println(genericSuperclass.getActualTypeArguments()[0]);
    }

}
 //java.util.Map<java.lang.String, java.lang.String>

Gson中泛型签名的应用

  • new TypeToken<List>() { }.getType()
    @Test
    public void test5() {
    
    
        Gson gson = new Gson();
        User user1 = new User(10, "bob");
        User user2 = new User(12, "jack");
        ArrayList<User> users = new ArrayList<>();
        users.add(user1);
        users.add(user2);
        //序列化(Java对象转换为字节序列的过程)
        String json = gson.toJson(users);
        System.out.println(json);
        //反序列化(把字节序列恢复为Java对象的过程)
        List<User> userList = gson.fromJson(json,
                new TypeToken<List<User>>() {
    
    
                }.getType());
        System.out.println(userList);
    }
    /*
     * [{"age":10,"name":"bob"},{"age":12,"name":"jack"}]
     * [User(age=10, name=bob), User(age=12, name=jack)]
     */
  • Gson中的源码
/*
客户端创建一个空的匿名子类。
这样做会将类型参数嵌入到匿名类的类型层次结构中,这样我们就可以在运行时重建它,而不用考虑擦除。
*/
  protected TypeToken() {
    
    
    this.type = getSuperclassTypeParameter(getClass());
    this.rawType = (Class<? super T>) $Gson$Types.getRawType(type);
    this.hashCode = type.hashCode();
  }
  
  //获取泛型类型
  static Type getSuperclassTypeParameter(Class<?> subclass) {
    
    
    Type superclass = subclass.getGenericSuperclass();
    if (superclass instanceof Class) {
    
    
      throw new RuntimeException("Missing type parameter.");
    }
    ParameterizedType parameterized = (ParameterizedType) superclass;
    return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
  }

泛型混淆,签名问题,混淆后签名找不到了,导致反射后拿不到

  • 保留签名信息
    -keepattributes Signature
  • Kotlin中
    -keep class kotlin.Metadata {*;}

参考文献

猜你喜欢

转载自blog.csdn.net/yu540135101/article/details/112998209