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

本文详细介绍了Java泛型的类型擦除机制,包括其产生的原因、优缺点以及在实际应用中的表现。讨论了泛型在编译后的对象类型转换开销、反射获取泛型信息的方法,同时通过Gson库中的泛型处理展示了类型信息的保存技巧。此外,还提及了混淆对泛型签名的影响及解决策略。
摘要由CSDN通过智能技术生成

类型擦除机制(编译时擦除为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中的源码
    创建子类可以获取到父类的泛型信息,所以需要创建一个匿名子类new TypeToken<List>() { }.getType()
/*
客户端创建一个空的匿名子类。
这样做会将类型参数嵌入到匿名类的类型层次结构中,这样我们就可以在运行时重建它,而不用考虑擦除。
创建子类可以获取到父类的泛型信息,所以需要创建一个匿名子类new TypeToken<List<User>>() { }.getType()
*/
  protected TypeToken() {
    this.type = getSuperclassTypeParameter(getClass());//此处获取type泛型的信息
    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 {*;}

参考文献

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值