【Java】泛型 桥方法

最近在看java的泛型,遇到桥方法,有点晦涩,理解一下。

首先,泛型在虚拟机的实现是通过类型擦除来做的,也就是说当一个泛型类被虚拟机编译后,其实已经没有泛型的概念了,只有基本的类。

而泛型擦除会与方法的多态产生冲突,这种冲突正是通过虚拟机生成的桥方法来解决的。

下面从一个例子入手:

定义一个泛型类和一个该类的子类:

class Container<T>{
    public void add(T t){
        System.out.println("fulei");
    }
}

class SubContainer extends Container<String>{
    public void add(String s){
        System.out.println("zilei");
    }
}
public class Main {
    public static void main(String args[]) {
        Container<String> c = new SubContainer();
        String s = "";
        c.add(s);
    }
}

在泛型类中,我们定义的add方法是参数化的,在子类中我们希望重写这个方法。看似没有问题。下面看下经过虚拟机编译后的原始类型,大概长这样:

class Container{
    public void add(Object t){
        System.out.println("fulei");
    }
}

class SubContainer extends Container{
    public void add(String s){
        System.out.println("zilei");
    }
}

然后我们看一下main方法里的调用过程:c变量实际类型是SubContainer,静态类型是Container。c.add()方法在父类Container暴露出的方法中只有add(Object t)方法,然后就再查找实际类型也即子类中有没有定义add(Object t)方法。答案是没有,那么就会调用父类的add(Object t)方法,这与我们的预期不一样,我们希望子类的add方法可以重写父类的方法。为什么没有重写成功呢?子类中我们只能定义add(String s)方法,这与父类的方法签名不同,明显是两个方法。更进一步是因为类型擦除以后导致父类的方法参数类型改变了,子类重写失败。

当然这仅仅是没有引入桥方法的情况。那么虚拟机又是如何通过引入桥方法解决上述问题的呢?

虚拟机会为子类生成一个桥方法:

public void add(Object t){
        add((String)t);
    }

方法的签名与父类一致,但是实现是通过子类的方法实现的,所以这个桥方法是最终被调用的方法。

有人可能会有一个问题:我们调用时传的参数是一个string,且子类中定义的就是add(String t),那为什么不能直接匹配到子类的add(String t)方法呢???

答案很简单,因为父类中没有这个方法,别忘了,我们c变量的静态类型是Container,是没有定义add(String t)这个方法的。也可以理解为暴露的api接口中没有add(String t),所以调不成功。

猜你喜欢

转载自blog.csdn.net/u010900754/article/details/81780238