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
- Compiled from "SubClass.java"
- public class com.mikan.SubClass implements com.mikan.SuperClass<java.lang.String> {
- public com.mikan.SubClass();
- flags: ACC_PUBLIC
- Code:
- stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #1 // Method java/lang/Object."<init>":()V
- 4: return
- LineNumberTable:
- line 7: 0
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 5 0 this Lcom/mikan/SubClass;
- public java.lang.String method(java.lang.String);
- flags: ACC_PUBLIC
- Code:
- stack=1, locals=2, args_size=2
- 0: aload_1
- 1: areturn
- LineNumberTable:
- line 11: 0
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 2 0 this Lcom/mikan/SubClass;
- 0 2 1 param Ljava/lang/String;
- public java.lang.Object method(java.lang.Object);
- flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
- Code:
- stack=2, locals=2, args_size=2
- 0: aload_0
- 1: aload_1
- 2: checkcast #2 // class java/lang/String
- 5: invokevirtual #3 // Method method:(Ljava/lang/String;)Ljava/lang/String;
- 8: areturn
- LineNumberTable:
- line 7: 0
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 9 0 this Lcom/mikan/SubClass;
- 0 9 1 x0 Ljava/lang/Object;
- }
- 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.class
- Classfile /Users/mikan/Documents/workspace/project/algorithm/target/classes/com/mikan/SuperClass.class
- Last modified 2015-8-7; size 251 bytes
- MD5 checksum 2e2530041f1f83aaf416a2ca3af9b7e3
- Compiled from "SuperClass.java"
- public interface com.mikan.SuperClass<T extends java.lang.Object>
- Signature: #7 // <T:Ljava/lang/Object;>Ljava/lang/Object;
- SourceFile: "SuperClass.java"
- minor version: 0
- major version: 51
- flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
- Constant pool:
- #1 = Class #10 // com/mikan/SuperClass
- #2 = Class #11 // java/lang/Object
- #3 = Utf8 method
- #4 = Utf8 (Ljava/lang/Object;)Ljava/lang/Object;
- #5 = Utf8 Signature
- #6 = Utf8 (TT;)TT;
- #7 = Utf8 <T:Ljava/lang/Object;>Ljava/lang/Object;
- #8 = Utf8 SourceFile
- #9 = Utf8 SuperClass.java
- #10 = Utf8 com/mikan/SuperClass
- #11 = Utf8 java/lang/Object
- {
- public abstract T method(T);
- flags: ACC_PUBLIC, ACC_ABSTRACT
- Signature: #6 // (TT;)TT;
- }
- localhost:mikan mikan$
- public abstract Object method(Object param);