在学习 MyBatisReflector 类时,我看到了一个方法 isBridge() ,我对此感到诧异,桥接方法是啥?从来只听过桥接模式?!!

后来一搜,woc,居然是泛型里面的知识(事实上我泛型学的并不好),那么:

  • 桥接方法是做什么用的
  • 桥接方法用在哪里
  • 我们能调用它吗

桥接方法是做什么的

说起泛型,还得从JDK巨变前说起(也就是JDK1.5的诞生),此前没有所谓的泛型参数,也没什么 List<String> 这种泛型类型,都是调用的 add(Object) 方法将变量加入的,也因此很容易在运行时产生错误。
而为了兼容那段没有泛型的日子,java的泛型也被阉割了很多(就是大名鼎鼎的擦除机制),也出现了一些隐藏在字节码中的方法(桥接方法)。
看下面一段代码:

  1. public abstract class Plate<T> {
  2. protected T t;
  3. abstract void set(T t);
  4. abstract T get();
  5. }
  6. public class ApplePlate extends Plate<Apple> {
  7. void set(Apple apple) {
  8. this.t = apple;
  9. }
  10. @Override
  11. Apple get() {
  12. return t;
  13. }
  14. }

当我们编译后

  1. // 编译后,因为擦除机制,泛型变量没了,父类全变成了Object
  2. public abstract class Plate {
  3. protected Object t;
  4. abstract void set(Object t);
  5. abstract Object get();
  6. }
  7. // 而ApplePlate中还保留着下面的方法,而且多出来了两个Object get() 和 void set(Object t)
  8. public class ApplePlate extends Plate {
  9. void set(Apple apple) {
  10. this.t = apple;
  11. }
  12. @Override
  13. Apple get() {
  14. return t;
  15. }
  16. // 桥接方法
  17. void set(Object object){
  18. this.t = (Apple)object;
  19. }
  20. // 桥接方法
  21. Object get(){
  22. return this.t;
  23. }
  24. }

我们的 ApplePlate 继承的 Plate 的泛型实参就被擦除了,而里面的所有 T 都被替换成了具体的类型。且多出来了两个参数和返回值类型均为 Object 的方法。没错了,他们就是桥接方法。

桥接方法用在哪里

因为有了擦除,我的父类在运行时没有了实际参数类型,都是 Object ,那我子类可没有实现 Object 类型的 get()set() ,那多态调用的时候,最终会调用父类的 getter/setter ,那么安全就得不到保障了。比如说:

  1. Plate plate = new ApplePlate();
  2. // 我是不是就会调用父类的void set(Object),并不是子类的set
  3. // 那是不是能把任何类型都设置给Plate中的t了
  4. plate.set(new Integer(11)); // 混过去了
  5. plate.set(new Double(11.2));// 混过去了,到时候会因为业务逻辑出问题,比如要用Apple的地方取到了一个Double

所以有了桥接方法之后:

  1. Plate plate = new ApplePlate();
  2. // 调用的是ApplePlate.set(Object)方法
  3. plate.set(new Integer(11)); // 运行时因为强转问题报错
  4. plate.set(new Double(11.2)); // 运行时因为强转问题报错

总结

桥接方法是擦除机制的一种补偿,为了完善类型安全而出现的。网上看到什么jdk1.8移除桥接方法,我移除*(龙门粗口),你用JDK1.8编译一下,看看字节码,是不是还存在?