在学习 MyBatis 的 Reflector 类时,我看到了一个方法 isBridge() ,我对此感到诧异,桥接方法是啥?从来只听过桥接模式?!!
后来一搜,woc,居然是泛型里面的知识(事实上我泛型学的并不好),那么:
- 桥接方法是做什么用的
- 桥接方法用在哪里
- 我们能调用它吗
桥接方法是做什么的
说起泛型,还得从JDK巨变前说起(也就是JDK1.5的诞生),此前没有所谓的泛型参数,也没什么 List<String> 这种泛型类型,都是调用的 add(Object) 方法将变量加入的,也因此很容易在运行时产生错误。
而为了兼容那段没有泛型的日子,java的泛型也被阉割了很多(就是大名鼎鼎的擦除机制),也出现了一些隐藏在字节码中的方法(桥接方法)。
看下面一段代码:
public abstract class Plate<T> {protected T t;abstract void set(T t);abstract T get();}public class ApplePlate extends Plate<Apple> {void set(Apple apple) {this.t = apple;}@OverrideApple get() {return t;}}
当我们编译后
// 编译后,因为擦除机制,泛型变量没了,父类全变成了Objectpublic abstract class Plate {protected Object t;abstract void set(Object t);abstract Object get();}// 而ApplePlate中还保留着下面的方法,而且多出来了两个Object get() 和 void set(Object t)public class ApplePlate extends Plate {void set(Apple apple) {this.t = apple;}@OverrideApple get() {return t;}// 桥接方法void set(Object object){this.t = (Apple)object;}// 桥接方法Object get(){return this.t;}}
我们的 ApplePlate 继承的 Plate 的泛型实参就被擦除了,而里面的所有 T 都被替换成了具体的类型。且多出来了两个参数和返回值类型均为 Object 的方法。没错了,他们就是桥接方法。
桥接方法用在哪里
因为有了擦除,我的父类在运行时没有了实际参数类型,都是 Object ,那我子类可没有实现 Object 类型的 get() 和 set() ,那多态调用的时候,最终会调用父类的 getter/setter ,那么安全就得不到保障了。比如说:
Plate plate = new ApplePlate();// 我是不是就会调用父类的void set(Object),并不是子类的set// 那是不是能把任何类型都设置给Plate中的t了plate.set(new Integer(11)); // 混过去了plate.set(new Double(11.2));// 混过去了,到时候会因为业务逻辑出问题,比如要用Apple的地方取到了一个Double
所以有了桥接方法之后:
Plate plate = new ApplePlate();// 调用的是ApplePlate.set(Object)方法plate.set(new Integer(11)); // 运行时因为强转问题报错plate.set(new Double(11.2)); // 运行时因为强转问题报错
总结
桥接方法是擦除机制的一种补偿,为了完善类型安全而出现的。网上看到什么jdk1.8移除桥接方法,我移除*(龙门粗口),你用JDK1.8编译一下,看看字节码,是不是还存在?
