在学习 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;
}
@Override
Apple get() {
return t;
}
}
当我们编译后
// 编译后,因为擦除机制,泛型变量没了,父类全变成了Object
public 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;
}
@Override
Apple 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编译一下,看看字节码,是不是还存在?