Android 开发也要掌握的Java知识 - Java 泛型擦除

1.什么是泛型擦除

  • 因为Java 泛型是1.5版本才引入的,为了向下兼容,其实虚拟机是不支持泛型的,Java里面是伪泛型机制
  • Java在编译期就擦除了所有泛型信息,这样Java就不需要产生新的类型到字节码,所有泛型类型最终都是一种类型

2.编译期如何擦除泛型:

1.检查泛型类型,获取目标类型
2.擦除类型变量,替换为限定类型
2.1如果泛型类型的类型变量没有限定,比如,就替换为Object类型为原始类型
2.2如果类型变量是,就替换为xxxClass类型为原始类型
2.3如果多个限定<T extends xClass1 & XClass2>,就用第一个xClass1类型为原始类型
3.必要的时候插入类型转换来保存类型安全
4.生成桥接方法,在扩展时保持多态性

3.泛型擦除也会有副作用

1.泛型擦除后替换为Object类型,所以不能传基本数据类型,比如List不能写int类型(List)
2.不能使用instanceof来判断类型,因为是Object嘛
3.类不能声明静态的成员变量,因为泛型参数要创建对象时就确定
等等其他

4.这里放黑马教程的截图,可以在哔哩哔哩上免费观看

无限制类型擦除.png

有限制类型擦除.png

擦除方法中类型定义的参数.png

桥接方法.png

5.验证泛型擦除

5.1用反射查看

public class Test<T extends Number> {
    T t;
 
    public T getT() {
        return t;
    }
 
    public void setT(T t) {
        this.t = t;
    }
 
    public <T extends Number> T show(T t) {
        return t;
    }
}
 
 
/*----------------------------------------------------------*/
 
public class TestObject<T> {
    T t;
 
    public T getT() {
        return t;
    }
 
    public void setT(T t) {
        this.t = t;
    }
 
    public <T> T show(T t){
        return t;
    }
}
 
 
/*----------------------------------------------------------*/

//利用反射,获取Test类的类对象
Test<Integer> test = new Test<>();
Class<? extends Test> clazz = test.getClass();
//获取成员变量
Field[] testField = clazz.getDeclaredFields();
for (Field field : testField) {
    Log.d(TAG, field.getName() + " : " + field.getType().getSimpleName());
}
Log.d(TAG, "-----------------------------------");
//成员方法
Method[] methods = clazz.getMethods();
for (Method method : methods) {
    Log.d(TAG, method.getName() + " : " + method.getReturnType().getSimpleName());
}


Log.d(TAG, "-----------------------------------");


TestObject<Integer> testObject = new TestObject<>();
Class<? extends TestObject> clz = testObject.getClass();
//获取成员变量
Field[] testObjectField = clz.getDeclaredFields();
for (Field field : testObjectField) {
    Log.d(TAG, field.getName() + " : " + field.getType().getSimpleName());
}
Log.d(TAG, "-----------------------------------");
//成员方法
Method[] objectMethods = clz.getMethods();
for (Method method : objectMethods) {
    Log.d(TAG, method.getName() + " : " + method.getReturnType().getSimpleName());
}

泛型有限制擦除.png

泛型无限制擦除.png

  • 我们可以看到 extends Number,反射拿到的类型就是Number,否则就是Object

5.2用字节码查看

  • Android studio 安装ASM Bytecode Viewer就能查看
    getT()返回的类型为Number
    show()方法类型为Number
    getT()返回类型为Object
    showT()方法类型为Object

6.验证泛型桥接

6.1用反射查看桥接方法

public interface Info<T> {
    T info(T t);
}
 
public class InfoImpl implements Info<Integer> {
    @Override
    public Integer info(Integer integer) {
        return integer;
    }
}
 
/*----------------------------------------------------------*/

InfoImpl info = new InfoImpl();
Class<InfoImpl> infoClazz = InfoImpl.class;
//获取成员变量
Field[] infoFields = infoClazz.getDeclaredFields();
for (Field field : infoFields) {
    Log.d(TAG, field.getName() + " : " + field.getType().getSimpleName());
}
Log.d(TAG, "-----------------------------------");
//成员方法
Method[] infoMethods = infoClazz.getMethods();
for (Method method : infoMethods) {
    Log.d(TAG, method.getName() + " : " + method.getReturnType().getSimpleName());
}

泛型桥接.png

  • 可以看到info方法有两个
6.2用字节码查看桥接方法

字节码查看桥接方法

7.参考文章

Java中的泛型擦除
哔哩哔哩视频

如果有写的不对,希望大家帮忙指出错误,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值