`
jiangshuiy
  • 浏览: 340026 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Java中的桥方法

阅读更多

 

Java中的桥方法是合成方法(synthetic methods),合成方法对于实现Java语言特征是必需的。最广为人知的例子就是协变返回类型和泛型中的案例,在泛型中案例基方法的参数被擦除后与实际被调用的方法不同时会使用到桥方法。

 

首先来看一个例子:

 

public class BridgeMethodOne {
    public static class BMOne<T> {
        public T getT() {
            return null;
        }
    }

    public static class BMTwo extends BMOne<String> {
        public String getT() {
            return null;
        }
    }
}

 

      事实上这仅仅是一个协变返回类型的例子,在类型擦除之后看起来和下面的片段类似:

 

public class BridgeMethodOne {
    public static class BMOne {
        public Object getT() {
            return null;
        }
    }

    public static class BMTwo extends BMOne {
        public String getT() {
            return null;
        }
    }
}

     然后在编译反编译之后,BMTwo将和下面类似:

 

public static class learn.generic.BridgeMethodOne$BMTwo extends learn.generic.BridgeMethodOne$BMOne {

    .

  // Method descriptor #15 ()Ljava/lang/String;

  // Stack: 1, Locals: 1

  public java.lang.String getT();

    0  aconst_null

    1  areturn

    

 

  // Method descriptor #16 ()Ljava/lang/Object;

  // Stack: 1, Locals: 1

  public bridge synthetic java.lang.Object getT();

    0  aload_0 [this]

    1  invokevirtual learn.generic.BridgeMethodOne$BMTwo.getT() : java.lang.String [17]

4  areturn

}

 

 

在上面可以看见有一个新的合成方法“java.lang.Object getT()”,这是源代码中没有的。这个方法作为一个桥方法,它所做的全部工作就是将调用代理到“java.lang.String getT()”。因为在JVM里,方法的返回类型是方法签名的一部分,而创建桥方法是实现协变返回类型的方式,因此编译器必须这么做。

现在再来看下面这个泛型指定的例子:

 

public class BridgeMethodTwo {
    public static class BMOne<T> {
        public T getT(T args) {
            return args;
        }
    }

    public static class BMTwo extends BMOne<String> {
        public String getT(String args) {
            return args;
        }
    }
}

 在编译之后,BMTwo将被转换成如下:

 

public static class learn.generic.BridgeMethodTwo$BMTwo extends learn.generic.BridgeMethodTwo$BMOne {

   public java.lang.String getT(java.lang.String args);

    0  aload_1 [args]

    1  areturn

    

  // Method descriptor #18 (Ljava/lang/Object;)Ljava/lang/Object;

  // Stack: 2, Locals: 2

  public bridge synthetic java.lang.Object getT(java.lang.Object arg0);

    0  aload_0 [this]

    1  aload_1 [arg0]

    2  checkcast java.lang.String [19]

    5  invokevirtual learn.generic.BridgeMethodTwo$BMTwo.getT(java.lang.String) : java.lang.String [21]

    8  areturn

}

 

 

 

在这里,桥方法重写了基类BMOne,它不仅做了有参数的调用,同时还执行了到“java.lang.String”的类型转换。这意味着在执行下面的代码忽略编译器的“uncheck”警告时,桥方法将抛出ClassCastException异常。

 

public static void main(String[] args) {
        BMOne one = new BMTwo();
        one.getT(new Object());
    }

 

 Exception in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.String

 

所列的两个例子是桥方法使用中最广为人知的,但除此之外,桥方法至少还有一个用武之地,那就是被用来改变基类方法的可见性。试着查看下面的例子并尝试着猜测编辑器需要在什么地方创建桥方法。

 

public class BridgeMethodThree {
    static class ClassA {
        public void foo() {
        }
    }

    public static class ClassB extends ClassA {
    }

    public static class ClassC extends ClassA {
        public void foo() {
        }
    }
} 

如果查看ClassB编译后的文件,可以发现发现:

public static class learn.generic.BridgeMethodThree$ClassB extends learn.generic.BridgeMethodThree$ClassA {

   public BridgeMethodThree$ClassB();

    0  aload_0 [this]

    1  invokespecial learn.generic.BridgeMethodThree$ClassA() [8]

    4  return

     

    public bridge synthetic void foo();

    0  aload_0 [this]

    1  invokespecial learn.generic.BridgeMethodThree$ClassA.foo() : void [15]

    4  return

 

}

 

因为ClassA是包级别限制的,不能被包外访问,而ClassB是公共的,而所有继承的方法必须能够被包外所访问,因此编译器需要桥方法。注意,由于ClassC重写了“foo”方法,不再需要桥方法来增加可见性。

也许还有一些其它地方使用了桥方法,但没有关于这些的源信息。同样,也没有桥方法的定义,虽然从上述的例子已经看出来并可以猜到大致代表什么,但JLS里却没有明确的说明。尽管isBridgejava1.5之后反射API了的公共方法,JVMJLS没有关于其的确切定义和编译器在何时、如何使用桥方法的定义。一般情况下,当一个类实现了一个参数化的接口或是继承了一个参数化的类时,需要引入桥方法。

 

英文参考:http://happyenjoylife.iteye.com/blog/1153964

              http://stas-blogspot.blogspot.com/2010/03/java-bridge-methods-explained.html

 

 

 

分享到:
评论

相关推荐

    Java中native方法学习

    在深入探讨Java中native方法的学习之前,我们先来理解一下native方法的概念以及它在Java中的作用。Native方法是Java编程语言中的一个特性,允许Java代码调用非Java代码(通常是C或C++编写的),这为Java应用程序提供...

    Java中给自己写的方法增加注释

    本篇文章将详细讲解如何在Java中给自己写的方法添加注释,以便于日后查阅和理解。 首先,我们了解Java中的三种基本注释方式: 1. 单行注释(//):这是最常用的注释方式,适用于单行的描述。 ```java // 这是一个...

    C语言调用java语言方法

    在Java代码中,你需要定义一个类,并在其中声明一个native方法。例如: ```java public class MyJavaClass { public native void callFromC(); static { System.loadLibrary("mylib"); } } ``` `callFromC...

    3.jni_c++调用java中的方法

    在本主题中,我们将深入探讨如何使用C++通过JNI来调用Java中的方法,以及如何实现C++与JavaScript的互调。这在跨平台开发、性能优化或利用现有C/C++库时尤其有用。 首先,我们需要理解JNI的基本结构。JNI接口定义了...

    java实例,通过jawin实现对dll中方法的调用

    Java是一种跨平台的编程语言,但在与底层操作系统交互时,比如调用Windows系统的动态链接库(DLL)中的方法,就需要一些特殊的工具和技术。本实例主要介绍如何使用Java和jawin库来实现DLL方法的调用。 jawin是一个...

    C#调用JAVA方法

    本篇文章将详细探讨如何在C#中调用Java方法,以及与之相关的技术,包括IKVM.NET。我们将通过提供的资源,即“UseJAVA.rar”和“JAVADemo.rar”,来了解具体的实现过程。 首先,让我们理解C#调用Java方法的基本原理...

    java本地方法接口

    1. **定义本地方法**:首先,在Java代码中定义一个本地方法(native method),该方法声明为native,表示它是由本地代码实现的。 ```java public native int add(int x, int y); ``` 2. **生成头文件**:使用`...

    Java类库中文手册

    这份手册详细介绍了Java标准类库中的各种类、接口、方法和异常,是Java学习者和开发者的得力助手。 在Java编程中,类库是预先编写好的一组类和接口的集合,它们提供了丰富的功能,可以帮助开发者构建高效、稳定的...

    android webview中使用Java调用JavaScript方法并获取返回值

    如果需要在WebView中启用JavaScript调用Android代码的功能,还要在addJavascriptInterface()方法中声明一个Java类的实例,并为其指定一个可以在JavaScript中访问的接口名字。 在Java代码中定义一个内部类,用于接收...

    java调用c#样例

    总之,这个示例提供了Java通过Jacob库调用C# DLL的方法,这对于那些需要在Java应用中利用.NET功能的开发者来说,是一个非常有价值的参考资源。通过理解Jacob的工作原理和实践中的调用流程,可以有效地实现Java和.NET...

    Java的COM桥 JCom技术文档资料

    总的来说,Java的COM桥JCom是Java开发者在Windows环境中利用COM组件的一个重要工具。它简化了Java与COM之间的通信,让Java开发者能够充分利用已有的COM资源,提升了开发效率。然而,使用JCom也需要注意性能和兼容性...

    关于java中的native方法

    Java中的Native方法是Java语言与本地(Native)代码交互的一种机制,主要涉及到Java的JNI(Java Native Interface)技术。JNI允许Java程序调用C、C++等非Java编写的代码,反之亦然,使得Java可以充分利用本地平台的...

    java代码桥连连接数据库

    本文将深入解析如何通过Java代码实现与数据库的连接,具体聚焦于“Java代码桥连连接数据库”这一主题,涵盖知识点包括:Java数据库连接(JDBC)的基本原理、关键代码实现细节以及最佳实践。 ### Java数据库连接...

    java连接数据库方法总结

    JDBC-ODBC桥是一种早期用于连接Java应用程序与各种数据库的方法。它通过ODBC(开放式数据库连接)来实现,ODBC是一种标准的数据库访问接口,主要用于Windows操作系统。 **1. 加载驱动** ```java Class.forName(...

    详解java 中泛型中的类型擦除和桥方法

    桥方法是Java编译器在编译泛型代码时,生成的一种特殊方法。桥方法的作用是保证多态性,例如: ```java public class Node&lt;T&gt; { public void setData(T data) { System.out.println("Node.setData"); this.data =...

    java中jython操作把python数据类型转成java类型

    在Java中,Jython是一个非常有用的工具,它允许我们在Java应用程序中嵌入Python代码,并能够无缝地在Python数据类型和Java数据类型之间进行转换。这极大地扩展了Java的生态系统,让我们能够利用Python丰富的库和简洁...

    Android之c调用java方法

    本教程将详细介绍如何在C语言中调用Java方法。 首先,了解JNI的基本概念。JNI是Java平台标准的一部分,允许Java代码和其他语言写的代码进行交互。当Java程序需要执行一些特定的、效率较高的本地操作时,就会使用JNI...

    在Java中使用VC++组件

    本地方法是声明在Java类中,但其实现是在本地代码(如C++)中。在Java程序中,我们用`native`关键字来声明本地方法,例如: ```java public class JNITest { static { System.loadLibrary("JNITest"); // 加载DLL...

    Java读取文件方法大全

    下面将详细讲解Java中读取文件的主要方法,并结合给出的代码片段进行分析。 首先,Java中最基础的文件读取方式是通过`java.io.File`类来创建文件对象,然后使用`java.io.FileInputStream`或`java.io....

Global site tag (gtag.js) - Google Analytics