类型擦除和桥接方法

589 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情

桥接方法

一提到桥接方法,最常见的应该是23种设计模式其中的1种,但是我们此处提到的桥接方法,并不设计模式,而是由于在JDK5中泛型的诞生而随之产生的。那么既然要提到桥接方法,就不得不先聊一下它所产生的前因——类型擦除。

类型擦除

泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,挡住向集合中插入非法数据。但编译器编译完带有泛型的java程序后,生成的class文件中将不再带有泛型信息,以此使程序运行效率不受到影响,这个过程称之为“类型擦除”。

下面我们以Animal作为接口,Cat作为实现,描述一下类型擦除。如下是泛型未被擦除的样子:

public interface Animal<T> {
    void eat(T t);
}

public class Cat implements Animal<String> {
    @Override
    public void eat(String s) {
        System.out.println("cat eat " + s);
    }
}

由于生成的class文件中将不带有泛型信息,那么我们将泛型擦除掉,以Object来代替

public interface Animal {
    void eat(Object t);
}

public class Cat implements Animal {
    @Override
    public void eat(String s) {
        System.out.println("cat eat " + s);
    }
}

整体处理方式如下图所示:

image.png

泛型被擦除掉了,我们大功告成了!!哎?不对啊!细心的同学们会发现,这个接口定义的是void eat(Object t),但是Cat类中Override的却是void eat(String s),这个是错误的呀,方法签名都不对,不能Override的啊。所以,我们发现,虽然泛型被擦除掉了,并且以Object来代替了,但是程序代码却是错误的了。那怎么办呢?OK,别着急,桥接方法它来了。

桥接方法

由于类型被擦除了,为了维持多态性,所以编译器就自动生成了桥接方法。如下图所示:

image.png

通过上图中红色框框住的方法我们可以看到,生成了一个void eat(Object s)方法,这样就可以维持Animal和Cat之间的Override关系。并且在这个方法内,我们通过将入参s强转型为String的方式,增加了对入参的限制,并且最终调用的方法依然是void eat(String s)这个方法。这么一看,泛型被擦除了,并且依然可以保证对入参类型的限制,完美!

不过,这时候对于一些严谨的同学们就会有质疑了,你说的这个真实存在吗?能证明给我们看吗?可以的。还记得反射那节课吧,给大家介绍过如何查看类中所有的方法,那么,我们就来查看一下Cat类中所有的方法有哪些吧。如下所示:

image.png

我们发现,通过Cat.class.getDeclaredMethods()方式获得Cat中的方法时,出现了一个根本不是我们编写的类——即:eat(Object o),那么该方法就是桥接方法了。

桥接方法的应用

上面介绍了类型擦除和桥接方法,那么会有同学们疑惑了,这东西有啥用呢?其实它的用处还真的不少呢,尤其是在应用框架上面,当需要使用反射方式访问方法时,就需要先过滤掉桥接方法,因为这个方法毕竟不是我们自己编写的嘛。下面我们来找一下桥接方法的身影吧。

MyBatis

桥接方法在Mybatis中的身影如下图所示:

image.png

Spring

桥接方法在Spring中的身影如下图所示:

image.png

今天的文章内容就这些了:

写作不易,笔者几个小时甚至数天完成的一篇文章,只愿换来您几秒钟的 点赞 & 分享 。

更多技术干货,欢迎大家关注公众号“爪哇缪斯” ~ (^o^)/ ~ 「干货分享,每天更新」