什么是桥接方法?Java中的BridgeMethod是编译器为了实现某些Java语言特性而自动生成的方法。我们可以通过Method类的isBridge方法来判断一个方法是否是桥接方法。在字节码文件中,桥接方法会被标记为ACC_BRIDGE和ACC_SYNTHETIC,其中ACC_BRIDGE表示该方法是编译器生成的桥接方法,ACC_SYNTHETIC表示该方法是编译器自动生成的。什么时候生成桥接方法?为实现哪些Java语言特性生成了桥接方法?最常见的两种情况是协变返回类型和类型擦除,因为它们导致超类方法的参数类型与实际调用的方法参数类型不一致。让我们通过两个例子更好地理解。协变返回类型协变返回类型是指子类方法的返回值类型不必严格等同于父类中重写方法的返回值类型,可以是更“具体”的类型。在Java1.5中,增加了对协变返回类型的支持,即子类重写父类方法时,返回类型可以是子类方法返回类型的子类。让我们看一个例子:publicclassParent{Numberget(){return1;}}publicclassChildextendsParent{@OverrideIntegerget(){return1;}}子类重写了父类Parent的get方法,Parent的get方法的返回类型是Number,而Child类中get方法的返回类型是Integer。编译反编译这段代码:javacChild.javajavap-v-cChild.class结果如下:publicclassChildextendsParent...省略部分结果...java.lang.Integerget();描述符:()Ljava/lang/Integer;标志:代码:stack=1,locals=1,args_size=10:iconst_11:invokestatic#2//方法java/lang/Integer.valueOf:(I)Ljava/lang/Integer;4:返回LineNumberTable:第5行:0java.lang.Numberget();描述符:()Ljava/lang/Number;标志:ACC_BRIDGE,ACC_SYNTHETIC代码:stack=1,locals=1,args_size=10:aload_01:invokevirtual#3//Methodget:()Ljava/lang/Integer;4:areturnLineNumberTable:line1:0从上面的结果可以看出,有一个方法java.lang.Numberget(),在源码中没有出现,是编译器自动生成的。这个方法被标记为ACC_BRIDGE和ACC_SYNTHETIC,也就是我们前面提到的桥接方法。这种方法起到了桥梁的作用。它所做的就是通过invokevirtual指令调用自身,然后调用方法java.lang.Integerget()。编译器这样做的原因是什么?因为在JVM方法中,返回类型也是方法签名的一部分,而桥接方法的签名与其父类的方法签名一致,从而实现了协变返回值类型。类型擦除泛型是Java1.5中引入的概念。在此之前,没有泛型的概念,但是泛型代码与以前版本的代码有很好的兼容性。为什么?这是因为Java编译器在编译时会将类型参数替换为其上界(类型参数中extends子句的类型)。如果没有定义上界,则默认为Object,称为类型擦除。当子类继承(或实现)父类(或接口)的泛型方法时,在子类中显式指定泛型类型,则编译器会在编译时自动生成一个桥接方法,例如:publicclassParent{voidset(Tt){}}publicclassChildextendsParent{@Overridevoidset(Stringstr){}}Child类继承父类Parent的泛型方法时,必须明确指定泛型类型为String,编译这段代码,然后反编译:publicclassChildextendsParent...省略部分结果...voidset(java.lang.String);描述符:(Ljava/lang/String;)V标志:代码:stack=0,locals=2,args_size=20:returnLineNumberTable:line5:0voidset(java.lang.Object);描述符:(Ljava/lang/Object;)Vflags:ACC_BRIDGE,ACC_SYNTHETICCode:stack=2,locals=2,args_size=20:aload_01:aload_12:checkcast#2//classjava/lang/String5:invokevirtual#3//Methodset:(Ljava/lang/String;)V8:returnLineNumberTable:line1:0从上面的结果可以看出,有一个方法voidset(java.lang.Object),在源码中没有出现,是由编译器自动生成的,这个方法被标记为ACC_BRIDGE和ACC_SYNTHETIC,也就是我们前面提到的桥接方法。这个方法作为一个桥梁,它所做的是通过invokevirtual指令将调用传递给自己,然后调用方法voidset(java.lang.String)。编译器这样做的原因是什么?因为父类在类型擦除之后变成了这样:publicclassParent