在最近的日常工作中,由于业务需要,笔者对Java字节码层面的知识进行了研究。具体需要根据类字节码获取具体方法名的方法入参,而源码中只有一个方法名。但是在实际使用中发现,当一个类实现了泛型接口时,在字节码层面,该类有两个同名的方法,导致无法判断哪个方法是我们需要的方法。经过研究发现,其中一个方法是编译器在编译时自动生成的桥接方法,可以通过特定的标识符来区分这两个方法。注意:这里的桥接方式与设计模式中的桥接方式不是一个概念。问题描述为了说明问题,作者将实际业务场景的具体案例模糊化,用一个稍微简单一点的能说明问题的例子来分析编译器自动生成的bridge方法。我们知道Java泛型是JDK5引入的新特性,被广泛使用。比如我们有一个运算符泛型接口Operator,接口中有一个process(Tt)方法,用于对输入参数T进行逻辑处理。示例代码如下:/***@authorrenzhiqiang*@date2022/2/2018:30*/publicinterfaceOperator{/***process方法*@paramt*/voidprocess(Tt);}在实际业务场景中,我们会有不同的Operator来实现Operator接口进行业务逻辑处理。那我们就创建一个具体的operator,实现Operator接口,重写process(Tt)方法。如下:/***用户信息运算符*@authorrenzhiqiang*@date2022/2/2018:30*/publicclassUserInfoOperatorimplementsOperator{@Overridepublicvoidprocess(Strings){//dosomething其中,泛型接口中的入参类型T被实现类中实际需要的类型java.lang.String代替。此时,我们已经准备好代码示例。那么,我们的目标是什么?就是获取UserInfoOperator#process(Strings)方法的参数类型java.lang.String。读到这里,读者可能会想:这不是很简单吗?通过反射,根据Class#getDeclaredMethods(),得到UserInfoOperator的所有方法,找到方法名为process的方法,然后得到参数列表。能否获取参数类型java.lang.String?如果正在阅读本文的您也这么认为,那么请继续阅读。根据Java反射方法Class#getDeclaredMethods()的描述:返回一个Method对象数组,包括public、protected、默认(包)访问、私有方法,但不包括继承方法。翻译过来就是:返回方法对象数组,包括公共方法、接受方法、受保护方法、默认(包)访问方法、私有方法,但不包括继承方法。根据我们的例子,如果我们通过反射使用Class#getDeclaredMethods()方法,在我们期望的返回方法数组中,应该只有一个名称为process的方法,但是这里有两个process方法。不意外,不意外!图debug发现UserInfoOperator类的两个process方法是因为编译器生成了bridge方法。我们知道,Java源代码需要经过编译器编译生成相应的.class文件,才能被JVM使用。在源码中,我们只定义了一个名为process的方法。然后我们考虑编译器在编译源代码的过程中是否会进行一些特殊的处理。为了更直观的查看编译后的字节码文件,在Idea中安装jclasslib插件,通过jclasslib查看UserInfoOperator和Operator的字节码。如下:图jclasslib查看UserInfoOperator类的字节码(第一种处理方法)图jclasslib查看UserInfoOperator类的字节码(第二种处理方法)图jclasslib查看Operator类的字节码通过jclasslib和.class文件查看发现,UserInfoOperator类中确实有两个流程方法:一个方法入参是java.lang.String,另一个方法入参是java.lang.Object。在Operator字节码中,只有一个process方法,方法的入参是java.lang.Object。同时,我们注意到在UserInfoOperator类的字节码中,[AccessFlag]项,方法的访问标志之一是[publicsyntheticbridge]。其中public很好理解,但是【合成桥】是从哪里来的呢?查阅相关资料后发现,标识符synthetic表示该方法是否由编译器自动生成;标识符bridge表示该方法是否为编译器生成的Bridging方法。图方法访问标志(来源:深入理解Java虚拟机(第三版))至此,可以确定其中一个过程方法是编译器自动生成的桥接方法。那么为什么编译器会生成桥接方法呢?而在什么情况下,会产生桥接方法呢?而如何判断一个方法是否是桥接方法呢?我们继续分析。为什么生成的bridge方法在源码中编译正确,Operator类的process方法的参数定义为process(Tt),参数类型为T。在字节码层面,我们可以看到process之后方法被编译时,编译器将输入参数类型更改为java.lang.Object。伪代码提示,大概是这样的:publicinterfaceOperator