java中什么是bridge method(桥接方法)
在看spring-mvc的源码的时候,看到在解析handler方法时,有关于获取桥接方法代码,不明白什么是桥接方法,经过查找资料,终于理解了什么是桥接方法。
什么是桥接方法
桥接方法是 JDK 1.5 引入泛型后,为了使Java的泛型方法生成的字节码和 1.5 版本前的字节码相兼容,由编译器自动生成的方法。
我们可以通过Method.isBridge()方法来判断一个方法是否是桥接方法,在字节码中桥接方法会被标记为ACC_BRIDGE和ACC_SYNTHETIC,其中ACC_BRIDGE用于说明这个方法是由编译生成的桥接方法,ACC_SYNTHETIC说明这个方法是由编译器生成,并且不会在源代码中出现。可以查看jvm规范中对这两个access_flag的解释http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.6。
有如下3个问题:
- 什么时候会生成桥接方法
- 为什么要生成桥接方法
- 如何通过桥接方法获取实际的方法
什么时候会生成桥接方法
那什么时候编译器会生成桥接方法呢?可以查看JLS中的描述http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.12.4.5。
就是说一个子类在继承(或实现)一个父类(或接口)的泛型方法时,在子类中明确指定了泛型类型,那么在编译时编译器会自动生成桥接方法(当然还有其他情况会生成桥接方法,这里只是列举了其中一种情况)。如下所示:
- package com.mikan;
- /**
- * @author Mikan
- * @date 2015-08-05 16:22
- */
- public interface SuperClass<T> {
- T method(T param);
- }
- package com.mikan;
- /**
- * @author Mikan
- * @date 2015-08-05 17:05
- */
- public class SubClass implements SuperClass<String> {
- public String method(String param) {
- return param;
- }
- }
来看一下SubClass的字节码:
- localhost:mikan mikan javap -c SubClass.class </span></span></li><li><span>Compiled from "SubClass.java" </span></li><li class="alt"><span>public class com.mikan.SubClass implements com.mikan.SuperClass<java.lang.String> { </span></li><li><span> public com.mikan.SubClass(); </span></li><li class="alt"><span> flags: ACC_PUBLIC </span></li><li><span> Code: </span></li><li class="alt"><span> stack=1, locals=1, args_size=1 </span></li><li><span> 0: aload_0 </span></li><li class="alt"><span> 1: invokespecial #1 // Method java/lang/Object."<init>":()V </span></li><li><span> 4: return </span></li><li class="alt"><span> LineNumberTable: </span></li><li><span> line 7: 0 </span></li><li class="alt"><span> LocalVariableTable: </span></li><li><span> Start Length Slot Name Signature </span></li><li class="alt"><span> 0 5 0 this Lcom/mikan/SubClass; </span></li><li><span> </span></li><li class="alt"><span> public java.lang.String method(java.lang.String); </span></li><li><span> flags: ACC_PUBLIC </span></li><li class="alt"><span> Code: </span></li><li><span> stack=1, locals=2, args_size=2 </span></li><li class="alt"><span> 0: aload_1 </span></li><li><span> 1: areturn </span></li><li class="alt"><span> LineNumberTable: </span></li><li><span> line 11: 0 </span></li><li class="alt"><span> LocalVariableTable: </span></li><li><span> Start Length Slot Name Signature </span></li><li class="alt"><span> 0 2 0 this Lcom/mikan/SubClass; </span></li><li><span> 0 2 1 param Ljava/lang/String; </span></li><li class="alt"><span> </span></li><li><span> public java.lang.Object method(java.lang.Object); </span></li><li class="alt"><span> flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC </span></li><li><span> Code: </span></li><li class="alt"><span> stack=2, locals=2, args_size=2 </span></li><li><span> 0: aload_0 </span></li><li class="alt"><span> 1: aload_1 </span></li><li><span> 2: checkcast #2 // class java/lang/String </span></li><li class="alt"><span> 5: invokevirtual #3 // Method method:(Ljava/lang/String;)Ljava/lang/String; </span></li><li><span> 8: areturn </span></li><li class="alt"><span> LineNumberTable: </span></li><li><span> line 7: 0 </span></li><li class="alt"><span> LocalVariableTable: </span></li><li><span> Start Length Slot Name Signature </span></li><li class="alt"><span> 0 9 0 this Lcom/mikan/SubClass; </span></li><li><span> 0 9 1 x0 Ljava/lang/Object; </span></li><li class="alt"><span>} </span></li><li><span>localhost:mikan mikan
- public Object method(Object param) {
- return this.method(((String) param));
- }
也就是说,桥接方法实际是是调用了实际的泛型方法,来看看下面的测试代码:
- package com.mikan;
- /**
- * @author Mikan
- * @date 2015-08-07 16:33
- */
- public class BridgeMethodTest {
- public static void main(String[] args) throws Exception {
- SuperClass superClass = new SubClass();
- System.out.println(superClass.method(”abc123”));// 调用的是实际的方法
- System.out.println(superClass.method(new Object()));// 调用的是桥接方法
- }
- }
- abc123
- Exception in thread ”main” java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.String
- at com.mikan.SubClass.method(SubClass.java:7)
- at com.mikan.BridgeMethodTest.main(BridgeMethodTest.java:27)
- at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
- at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
- at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
- at java.lang.reflect.Method.invoke(Method.java:606)
- at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
- SuperClass<String> superClass = new SubClass();
为什么要生成桥接方法
上面看到了编译器在什么时候会生成桥接方法,那为什么要生成桥接方法呢?
在java1.5以前,比如声明一个集合类型:
- List list = new ArrayList();
- List<String> list = new ArrayList<String>();
- localhost:mikan mikan javap -c -v SuperClass.</span><span class="keyword">class</span><span> </span></span></li><li><span>Classfile /Users/mikan/Documents/workspace/project/algorithm/target/classes/com/mikan/SuperClass.<span class="keyword">class</span><span> </span></span></li><li class="alt"><span> Last modified <span class="number">2015</span><span>-</span><span class="number">8</span><span>-</span><span class="number">7</span><span>; size </span><span class="number">251</span><span> bytes </span></span></li><li><span> MD5 checksum 2e2530041f1f83aaf416a2ca3af9b7e3 </span></li><li class="alt"><span> Compiled from <span class="string">"SuperClass.java"</span><span> </span></span></li><li><span><span class="keyword">public</span><span> </span><span class="keyword">interface</span><span> com.mikan.SuperClass<T </span><span class="keyword">extends</span><span> java.lang.Object> </span></span></li><li class="alt"><span> Signature: #<span class="number">7</span><span> </span><span class="comment">// <T:Ljava/lang/Object;>Ljava/lang/Object;</span><span> </span></span></li><li><span> SourceFile: <span class="string">"SuperClass.java"</span><span> </span></span></li><li class="alt"><span> minor version: <span class="number">0</span><span> </span></span></li><li><span> major version: <span class="number">51</span><span> </span></span></li><li class="alt"><span> flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT </span></li><li><span>Constant pool: </span></li><li class="alt"><span> #<span class="number">1</span><span> = Class #</span><span class="number">10</span><span> </span><span class="comment">// com/mikan/SuperClass</span><span> </span></span></li><li><span> #<span class="number">2</span><span> = Class #</span><span class="number">11</span><span> </span><span class="comment">// java/lang/Object</span><span> </span></span></li><li class="alt"><span> #<span class="number">3</span><span> = Utf8 method </span></span></li><li><span> #<span class="number">4</span><span> = Utf8 (Ljava/lang/Object;)Ljava/lang/Object; </span></span></li><li class="alt"><span> #<span class="number">5</span><span> = Utf8 Signature </span></span></li><li><span> #<span class="number">6</span><span> = Utf8 (TT;)TT; </span></span></li><li class="alt"><span> #<span class="number">7</span><span> = Utf8 <T:Ljava/lang/Object;>Ljava/lang/Object; </span></span></li><li><span> #<span class="number">8</span><span> = Utf8 SourceFile </span></span></li><li class="alt"><span> #<span class="number">9</span><span> = Utf8 SuperClass.java </span></span></li><li><span> #<span class="number">10</span><span> = Utf8 com/mikan/SuperClass </span></span></li><li class="alt"><span> #<span class="number">11</span><span> = Utf8 java/lang/Object </span></span></li><li><span>{ </span></li><li class="alt"><span> <span class="keyword">public</span><span> </span><span class="keyword">abstract</span><span> T method(T); </span></span></li><li><span> flags: ACC_PUBLIC, ACC_ABSTRACT </span></li><li class="alt"><span> Signature: #<span class="number">6</span><span> </span><span class="comment">// (TT;)TT;</span><span> </span></span></li><li><span>} </span></li><li class="alt"><span>localhost:mikan mikan
- public abstract Object method(Object param);