java泛型:类型擦除

虽然编写代码时可以使用泛型,但是泛型实际上是一种语法糖,虚拟机并不认识泛型,编译器在编译时会进行类型擦除。

  • 无限定的类型变量,替换为Object类型

  • 使用了限定符的类型变量,替换为对应的限定类型。

    <T extends Comparable>替换为Comparable

    <T extends Serializable & Comparable>,多类型限定,类型擦除时,替换为第一个类型Serializable。编译器将在有必要的时候向Comparable插入强制类型转换。为了提高效率,应该将标签接口(没有方法的接口)放在后面。

  • 类型擦除后,为了保证类型安全,必要时将插入强制类型转换。

    例如:

    ArrayList<String> list = new ArrayList<>();
    list.add("no");
    String str = list.get(0);
    // 类型擦除后,第三行其实为:String str = (String) list.get(0);
    

    假若没有泛型,我们将要自行进行强制类型转换,假如我们不小心加入了一个File对象,这就会抛出异常。


上面是泛型工作的基本机制,不过,在翻译泛型方法时,会出现比较复杂的问题。

例如:

public class Pet<T> {
    private T arg;
    public void setArg(T arg) {
        this.arg = arg;
    }
    public T getArg() {
        return arg;
    }
}

class Cat extends Pet<String> {
    @Override
    public void setArg(String arg) {
        ...
    }
    
    @Override
    public String getArg() {
        ...
    }
}

假如我们这样调用:

Pet<String> pet = new Cat();
pet.setArg("cocoa");
String str = pet.getArg();

这里希望对setArg的调用具有多态性,调用最合适的方法。引用的是Cat类型,那么应该调用Cat.setArg。问题在于多态和类型擦除产生了冲突,要解决这个问题,就需要编译器在Cat类中生成一个桥方法。

public void setArg(Object arg) {
    setArg((String) arg);
}

补充: 其实也可以这样理解,类型擦除后,Pet.setArg(Object)与子类试图重载的方法Cat.setArg(String)拥有了不一样的签名,在那一行调用pet.setArg("cocoa")的代码中,只会调用父类的Pet.setArg(Object)而不是调用子类Cat.setArg(String)这个方法。为了解决这个问题,编译器会在子类Cat加入如上所示的桥方法,强制类型转换后去调用Cat.setArg(String)


因为类型擦除的原因,Cat类重写Pet.getArg()时,编译时也会多出现一个桥方法,这才是实际上重写了父类Pet.getArg()的方法。

public Object getArg() {
    ...
}

此时在类中出现了两个签名相同的方法!

String getArg()
Object getArg()

虽然不能这样编写java代码(无法通过编译)。但是,在虚拟机中,用参数类型和返回类型确定一个方法。因此, 编译器可能产生两个仅返回类型不同的方法字节码,虚拟机能够正确地处理这一情况。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值