`

java中什么是bridge method(桥接方法)

    博客分类:
  • Java
 
阅读更多

在看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$

 SubClass只声明了一个方法,而从字节码可以看到有三个方法,第一个是无参的构造方法(代码中虽然没有明确声明,但是编译器会自动生成),第二个是我们实现的接口中的方法,第三个就是编译器自动生成的桥接方法。可以看到flags包括了ACC_BRIDGE和ACC_SYNTHETIC,表示是编译器自动生成的方法,参数类型和返回值类型都是Object。再看这个方法的字节码,它把Object类型的参数强制转换成了String类型,再调用在SubClass类中声明的方法,转换过来其实就是:

    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()));// 调用的是桥接方法
    }

}

 这里声明了SuperClass类型的变量指向SubClass类型的实例,典型的多态。在声明SuperClass类型的变量时,不指定泛型类型,那么在方法调用时就可以传任何类型的参数,因为SuperClass中的方法参数实际上是Object类型,而且编译器也不能发现错误。在运行时当参数类型不是SubClass声明的类型时,会抛出类型转换异常,因为这时调用的是桥接方法,而在桥接方法中会进行强制类型转换,所以才会抛出类型转换异常。上面的代码输出结果如下:

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类型的变量就指定了泛型类型:

SuperClass<String> superClass = new SubClass();

 当然这里类型只能是String,因为SubClass的泛型类型声明是String类型的,如果指定其他类型,那么在编译时就会错误,这样就把类型检查从运行时提前到了编译时。这就是泛型的好处。

 

为什么要生成桥接方法

上面看到了编译器在什么时候会生成桥接方法,那为什么要生成桥接方法呢?

在java1.5以前,比如声明一个集合类型:

List list = new ArrayList(); 

 那么往list中可以添加任何类型的对象,但是在从集合中获取对象时,无法确定获取到的对象是什么具体的类型,所以在1.5的时候引入了泛型,在声明集合的时候就指定集合中存放的是什么类型的对象:

List<String> list = new ArrayList<String>(); 

 那么在获取时就不必担心类型的问题,因为泛型在编译时编译器会检查往集合中添加的对象的类型是否匹配泛型类型,如果不正确会在编译时就会发现错误,而不必等到运行时才发现错误。因为泛型是在1.5引入的,为了向前兼容,所以会在编译时去掉泛型(泛型擦除),但是我们还是可以通过反射API来获取泛型的信息,在编译时可以通过泛型来保证类型的正确性,而不必等到运行时才发现类型不正确。由于java泛型的擦除特性,如果不生成桥接方法,那么与1.5之前的字节码就不兼容了。如前面的SuperClass中的方法,实际在编译后的字节码如下:

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$

 通过Signature: #7   // <T:Ljava/lang/Object;>Ljava/lang/Object;可以看到,在编译完成后泛型实际上就成了Object了,所以方法实际上成了

public abstract Object method(Object param); 

 而SubClass实现了SuperClass这个接口,如果不生成桥接方法,那么SubClass就没有实现接口中声明的方法,语义就不正确了,所以编译器才会自动生成桥接方法,来保证兼容性。

 

如何通过桥接方法获取实际的方法

 

我们在通过反射进行方法调用时,如果获取到桥接方法对应的实际的方法呢?可以查看spring中org.springframework.core.BridgeMethodResolver类的源码。实际上是通过判断方法名、参数的个数以及泛型类型参数来获取的。

分享到:
评论

相关推荐

    java设计模式教程+源代码

    Bridge ( 桥接模式 ) Composite ( 组合模式 ) Decorator ( 装饰模式 ) Facade ( 外观模式 ) Flyweight ( 享元模式 ) Proxy ( 代理模式 ) Chain of Responsibility ( 责任链模式 ) Command ( 命令模式 ) ...

    java之23种设计模式

    7. Bridge(桥接) 8. Composite(组合) 9. Decorator(装饰) 10. Facade(外观) 11. Flyweight(享元) 12. Proxy(代理) 行为型 13. Interpreter(解释器) 14. Template Method(模板方法) 15. Chain of ...

    java设计模式

    7. Bridge(桥接) 8. Composite(组合) 9. Decorator(装饰) 10. Facade(外观) 11. Flyweight(享元) 12. Proxy(代理) 行为型 13. Interpreter(解释器) 14. Template Method(模板方法) 15. Chain of ...

    [Java设计模式(第2版)(Design.Patterns.in.Java).John.Metsker

    第6章 桥接(bridge)模式 52 第7章 职责型模式介绍 62 第8章 单例(singleton)模式 67 第9章 观察者(observer)模式 72 第10章 调停者(mediator)模式 85 第11章 代理(proxy)模式 97 第12章 职责链(chain of ...

    Java面试 java设计模式整理 单例、工厂、策略等 面试使用,学习使用

    10.桥接模式(Bridge) 7 11.组合模式(Composite) 8 12.享元模式(Flyweight) 9 13.模板模式 9 13、策略模式(strategy) 12 14、模板方法模式(Template Method) 13 15、观察者模式(Observer) 14 16、迭代子...

    Java高手真经 - Java Web系统设计与架构 源代码(一)设计模式

    pattern/src/structure/bridge //12.5桥接模式 pattern/src/structure/composite //12.6组合模式 pattern/src/structure/flyweight //12.7享元模式 pattern/src/behavior/strategy //13.1策略模式 pattern/src/...

    JAVA设计模式-day2.pdf

    JAVA设计模式-day2,请的行业大能讲得课程,涉及:创建模式(5种: 1、 工厂方法模式(Factory Method); 2、 抽象工厂模式; 3、 单例模式(Singleton) • 4、 建造者模式(Builder); 5、 原型模式(Prototype...

    JAVA经典设计模式大全

    4.2 Bridge(桥接)—对象结构型 模式 100 4.3 Composite(组成)—对象结构型 模式 107 4.4 Decorator(装饰)—对象结构型 模式 115 4.5 FACADE(外观)—对象结构型 模式 121 4.6 Flyweight(享元)—...

    word源码java-java-knowledge-collection:Java核心知识点梳理

    桥接模式(Bridge) 组合模式(Composite) 装饰模式(Decorator) 外观模式(Facade) 享元模式(Flyweight) 代理模式(Proxy) 行为型 职责链模式(Chain of Responsibility) 命令模式(Command) 解释器模式...

    java版本二十三种设计模式.zip

    - 桥接模式(Bridge) - 组合模式(Composite) - 外观模式(Facade) - 享元模式(Flyweight) - 观察者模式(Observer) - 模板方法模式(Template Method) - 策略模式(Strategy) - 责任链模式(Chain of ...

    Java设计模式,并加上个人理解

    1. 设计模式 1.1 含义 1.2 作用 1.3 设计原则 1.4 分类 2. 简单工厂模式 (SimpleFactoryPattern) 3. 工厂方法模式 (Factory Method) ...16. 桥接模式 (Bridge Pattern) 17. 观察者模式 (Observer Pattern)

    design-pattern-java.pdf

    桥接模式-Bridge Pattern 处理多维度变化——桥接模式(一) 处理多维度变化——桥接模式(二) 处理多维度变化——桥接模式(三) 处理多维度变化——桥接模式(四) 组合模式-Composite Pattern 树形结构的处理...

    设计模式文档

    4.2 Bridge(桥接)—对象结构型 模式 100 4.3 Composite(组成)—对象结构型 模式 107 4.4 Decorator(装饰)—对象结构型 模式 115 4.5 FACADE(外观)—对象结构型 模式 121 4.6 Flyweight(享元)—对象结构型 ...

    java高手真经 (UML建模+设计模式+面向服务架构) 卷1

    pattern/src/structure/bridge //12.5桥接模式 pattern/src/structure/composite //12.6组合模式 pattern/src/structure/flyweight //12.7享元模式 pattern/src/behavior/strategy //13.1策略模式 pattern/src/...

    PatternViaJava:设计模式_Java实现

    设计模式(java版) 模式是在某情景(context)下,针对某问题的某种解决方案 **总览图** ![设计模式预览]() *创建型* Singleton(单例模式):...Bridge(桥接模式):将抽象部分与它的实现部分分离,使它们都可以独立地变

    java高手真经 (UML建模+设计模式+面向服务架构) 卷9

    pattern/src/structure/proxy //12.3代理模式 pattern/src/structure/facade //12.4外观模式 pattern/src/structure/bridge //12.5桥接模式 pattern/src/structure/composite //12.6组合模式 pattern/src/structure/...

    DesignPatterns

    Bridge(桥接) Composite(组合) [Done] Decorator(装饰) Facade(外观) Flyweight(享元) Proxy(代理) ##行为型 Interpreter(解释器) [Done] Template Method(模板方法) Chain of Responsibility(责任...

    java高手真经 (UML建模+设计模式+面向服务架构) 卷8

    pattern/src/structure/proxy //12.3代理模式 pattern/src/structure/facade //12.4外观模式 pattern/src/structure/bridge //12.5桥接模式 pattern/src/structure/composite //12.6组合模式 pattern/src/structure/...

    深入浅出设计模式(中文版电子版)

    4.2BridgePattern(桥接模式) 104 4.2.1定义 104 4.2.2现实中的实例——男人的约会 106 4.2.3C#实例——商业对象与数据对象 107 4.2.4Java实例——不同系统的图像处理 112 4.2.5优势和缺陷 114 4.2.6应用情景...

    java高手真经 (UML建模+设计模式+面向服务架构) 卷3

    pattern/src/structure/bridge //12.5桥接模式 pattern/src/structure/composite //12.6组合模式 pattern/src/structure/flyweight //12.7享元模式 pattern/src/behavior/strategy //13.1策略模式 pattern/src/...

Global site tag (gtag.js) - Google Analytics