Java OpenResty Spring Spring Boot MySQL Redis MongoDB PostgreSQL Linux Android Nginx 面试 小程序 Arthas JVM AQS juc Kubernetes Docker DevOps


Java 语法糖 - 方法重写时的桥接方法

Java 大约 3711 字

桥接方法

子类重写的方法的返回值可以是父类方法的返回值的子类

本质

编译器帮忙生成了合成的桥接方法。

内部有一个重写父类相同返回值的方法,以及子类重写的返回值类型的方法。

Java 代码

子类Bmethod方法的返回值是Integer类型是父类Amethod方法返回值Number类型的子类。

class A {
    public Number method() {
        return 1;
    }
}

class B extends A {
    @Override
    public Integer method() { // Integer 是 Number 的子类
        return 2;
    }
}

编译后

通过IDEA中的View-Show Bytecode可参看字节码(带了syntheticbridge关键字)。

(此处的方法是根据反编译后的字节码推得的伪代码)

class B extends A {
    public Integer method() {
        return 2;
    }

    // 真正重写父类的 Number 方法
    public synthetic bridge Number method() {
        return method(); // 调用自身的 method 方法
    }
}

代码验证

可以看到getDeclaredMethods方法获得的两个方法。

public class Test9 {

    public static void main(String[] args) throws NoSuchMethodException {
        Method[] methods = B.class.getDeclaredMethods();
        for (Method m : methods) {
            System.out.println("method name#" + m.getName() + ", returnType#" + m.getReturnType() + ", isSynthetic#" + m.isSynthetic() + ", isBridge#" + m.isBridge() + ", method#" + m);
        }
    }

}

输出

method name#method, returnType#class java.lang.Integer, isSynthetic#false, isBridge#false, method#public java.lang.Integer com.example.B.method()
method name#method, returnType#class java.lang.Number, isSynthetic#true, isBridge#true, method#public java.lang.Number com.example.B.method()

IDEA 生成的字节码

可以看到IDEA直接帮忙生成了可读性更好的public synthetic bridge method()方法。

// class version 52.0 (52)
// access flags 0x20
class com/example/B extends com/example/A {

  // compiled from: B.java

  // access flags 0x0
  <init>()V
   L0
    LINENUMBER 3 L0
    ALOAD 0
    INVOKESPECIAL com/example/A.<init> ()V
    RETURN
   L1
    LOCALVARIABLE this Lcom/example/B; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x1
  public method()Ljava/lang/Integer;
   L0
    LINENUMBER 6 L0
    ICONST_2
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
    ARETURN
   L1
    LOCALVARIABLE this Lcom/example/B; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x1041
  public synthetic bridge method()Ljava/lang/Number;
   L0
    LINENUMBER 3 L0
    ALOAD 0
    INVOKEVIRTUAL com/example/B.method ()Ljava/lang/Integer;
    ARETURN
   L1
    LOCALVARIABLE this Lcom/example/B; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1
}

javap 生成的字节码

可以看到flags中的ACC_BRIDGE, ACC_SYNTHETIC两个标识。

{
  com.example.B();
    descriptor: ()V
    flags: (0x0000)
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method com/example/A."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/example/B;

  public java.lang.Integer method();
    descriptor: ()Ljava/lang/Integer;
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: iconst_2
         1: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
         4: areturn
      LineNumberTable:
        line 6: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/example/B;

  public java.lang.Number method();
    descriptor: ()Ljava/lang/Number;
    flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokevirtual #3                  // Method method:()Ljava/lang/Integer;
         4: areturn
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/example/B;
}
SourceFile: "B.java"
阅读 332 · 发布于 2022-04-27

————        END        ————

Give me a Star, Thanks:)

https://github.com/fendoudebb

扫描下方二维码关注公众号和小程序↓↓↓

扫描二维码关注我
昵称:
随便看看 换一批