Bridge Method (桥接方法)

Bridge Method直译过来就是桥接方法。从字面意思可以很容易的理解出这个方法是在连接两个东西。那到底是在连接什么呢?废话少说,上代码:

abstract class A<T> {
abstract T get(T t);
}

class B extends A<String> {
@Override
String get(String s) {
return "";
}
}

public class TestBridge {
public static void main(String[] args) {
Class<B> clazz = B.class;
Method[] methods = clazz.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
Method m = methods[i];
System.out.println(getMethodInfo(m) + " is Bridge Method? " + m.isBridge());
}
}

public static String getMethodInfo(Method m){
StringBuilder sb = new StringBuilder();
sb.append(m.getReturnType()).append(" ");
sb.append(m.getName());
Class[]params = m.getParameterTypes();
for (int i = 0; i < params.length; i++) {
sb.append(params[i].getName()).append(" ");
}
return sb.toString();
}
}

运行上面的代码,可以看到输出结果如下:
[class java.lang.String get] is Bridge Method? false
[class java.lang.Object get] is Bridge Method? true

或许你现在诧异了,怎么B类中只定义了一个方法,现在怎么出现了两个呢?而且返回类型和参数类型还不同。话说这个是java5中的泛型所带来的结果了。针对上面的这段代码分析下:
在java5之前,你可以往一个集合里扔任何你想扔的对象。往集合中放对象的人很爽了。但是从集合中去对象的人就头大了。你不知道你下个取到的对象将会是什么类型的。不知道转成什么类型,你也就只能使用所有Object的方法了,这样就毫无意义了。所以在java5中提供了泛型这一新特性。程序员在写代码的时候可以指定集合可以存放对象的类型。然后将这些类型检查的事情交给编译器去做,减少了程序员的工作。
上面代码中<>中的T和String就是指定类的参数类型。T代表一种泛型,告诉编译器,一旦有类指定了T这个参数的实际类,那么get方法返回的类型也必须为同一个类(当然也可以是这个类的子类;这个也是java5中的协变式返回新特性),如果不是,就必须报错提示;将原来的运行时可能出现的错误提前到编译期了。那么,假设你是java5编译器的设计者,你会如何来设计让编译器能实现这个特性,同时能保证编译出来的字节码可以在老版本的jdk中运行呢?java5编译器中作了个很巧妙的设计——桥接方法。

下面来说说java5编译器是如何来编译上面的代码:

abstract class A<T> {
abstract T get(T t);
}

对于A类,编译器看到<>中指定的T参数后,会用Object把类中的其他T参数替换。因为在jdk中根本就不存在T这个类嘛。替换后就成了下面这样子。

abstract class A {
abstract Object get(Object obj);
}

上面这个过程称为类型擦除。对于B类,它继承了A类,指定了T参数为String。如果还按照以前那么编译,那编译的类就是:

class B extends A {
String get(String s) {
return "";
}
}

这样在运行时肯定会报错,因为B继承了A,而A又是asbtract类,B还没overriding A中Object get()方法。如何解决这个错误呢?java5编译器在编译的时候做了些手脚。当编译器发现你指定了类型参数,便会在编译的字节码中添加一个桥接方法。这个可以查看B的反编译代码就知道了。

class B extends A {
//编译器添加的方法
Object get(Object s) {
return (Object) get((String) s);
}

String get(String s) {
return "";
}
}

那么编译器会在什么情况下添加桥接方法呢?这个就不细说了,感兴趣的可以自己尝试。下面再说说协变式返回,什么是协变式返回呢?先可以对比下java1.4和java5中对于重写的定义:
In Java 1.4, and earlier, one method can override another if the signatures match exactly.
In Java 5, a method can override another if the arguments match exactly but the return type of the overriding method, if it is a subtype of the return type of the other method.
也就是说,对于重写的判断放宽了条件,子类中方法返回的类型是父类中方法返回类型的子类也是重写,听起来有点绕,看下面这段代码:

class A{
Father get(){}
}
class B extends A{
@Override
Son get(){}
}
class Father{}
class Sun extends Father{}

也就是B中的get()方法返回Son类也是重载。如何实现的呢?同样是使用桥接方法。

关于桥接方法更多的可以参考JLS
[url=http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.4.5]Create Frame, Synchronize, Transfer Control[/url]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值