为什么Java中的bridge方法不能防止这种情况并引发异常?

时间:2019-02-15 12:38:22

标签: java generics

为什么下面的泛型代码在运行时不引发异常?

public static void main(String[] args) {
        ((List)new ListString()).add(3);
    }


    static class ListString extends ArrayList<String>{

    }

我了解这不是应该使用泛型的方法,但我认为the bridge methods可以通过添加以下方法来解决此问题,该方法将抛出ClassCastException.

public boolean add(Object o){
      return add((E)o);
}

注意:如果我根据以下内容修改ListString类,则确实会引发ClassCastException:

static class ListString extends ArrayList<String>{
        @Override
        public boolean add(String o){
            return super.add(o);
        }
    }

1 个答案:

答案 0 :(得分:4)

除非您实际编写方法,否则不会创建桥(合成方法),就像在后续测试中所做的那样,您在代码中将public boolean add(String o){return super.add(o);}放入了代码。

当您对类型为.get(someInt)的表达式调用StringList时,编译器可以看到您的StringList类型,即使它是ArrayList<String>的子类型,也不会没有任何带有签名String get(int)的方法。因此,它将在字节码中生成对Object get()方法的调用,并(如果需要)强制转换,并且在Java方面,它的作用就像StringList上的get方法返回一样串。即使在字节码级别也没有。

如果您随后使String get(int)方法出现(例如,通过子类化StringList或通过编辑源代码并仅重新编译该文件),则仅会生成bridge方法。因为java是动态调度,所以始终使用该桥。桥强制转换并调用(或调用并强制转换,取决于该桥是覆盖返回类型的诡计还是参数诡计)。

您可以使用javap -v来验证所有这些:我强烈建议所有发现这一点有趣并想准确了解其工作原理的人,在探索两者之间的输出差异方面会很有趣:

import java.util.*;

public class Test extends ArrayList<String> {
    public static void main(String[] args) {
        List raw = new Test();
        raw.add(3);
    }
}

这:

import java.util.*;

public class Test extends ArrayList<String> {
    public static void main(String[] args) {
        List raw = new Test();
        raw.add(3);
    }
    public boolean add(String o) {
        return super.add(o);
    }
}