Heim >Java >javaLernprogramm >So verwenden Sie die Java-Bridging-Methode

So verwenden Sie die Java-Bridging-Methode

王林
王林nach vorne
2023-04-20 13:07:111095Durchsuche

1. Einführung in die Bridge-Methode

Die Bridge-Methode folgt der Einführung von Generika in jdk1.5, um den von der generischen Java-Methode generierten Bytecode mit dem vorherigen Bytecode kompatibel zu machen jdk1.5 Automatisch vom Compiler generiert.

Sie können method.isBridge() verwenden, um festzustellen, ob es sich bei der Methode um eine Bridge-Methode handelt. Im generierten Bytecode sind Flags mit der Bezeichnung ACC_BRIDGE und ACC_SYNTHETIC vorhanden - Tiefes Verständnis der Java Virtual Machine. Aus einem Zugriffsflagdiagramm können Sie ersehen, dass die ACC_BRIDGE-Darstellungsmethode eine vom Compiler generierte Bridge-Methode ist und die ACC_SYNTHETIC-Darstellungsmethode automatisch vom Compiler generiert wird und nicht zum Quellcode gehört . method.isBridge()判断method是否是桥接方法,在生成的字节码中会有flags标记 ACC_BRIDGE, ACC_SYNTHETIC ,根据来自深入理解java虚拟机的一张访问标志图可以看到 ACC_BRIDGE表示方法是由编译器产生的桥接方法,ACC_SYNTHETIC表示方法由编译器自动产生不属于源码。

So verwenden Sie die Java-Bridging-Methode

2. 什么时候会生成桥接方法

当子类继承父类(继承接口)实现抽象泛型方法的时候,编译器会为子类自动生成桥接方法

#父类
public abstract class SuperClass<T> {

  public abstract T get(T t) ;
}


#子类
public class SubClass extends SuperClass<String> {

  @Override
  public String get(String s) {
    return s;
  }
}

使用javap -v SubClass.class命令查看类SubClass的字节码:

Classfile /Users/xudong/project-maven/test/person-study/dubbo-provider/target/classes/com/monian/dubbo/provider/study/generic/SubClass.class
  Last modified 2022年7月25日; size 777 bytes
  MD5 checksum 1328a7043cde4b809a156e7a239335a6
  Compiled from "SubClass.java"
public class com.monian.dubbo.provider.study.generic.SubClass extends com.monian.dubbo.provider.study.generic.SuperClass<java.lang.String>
  minor version: 0
  major version: 52
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #4                          // com/monian/dubbo/provider/study/generic/SubClass
  super_class: #5                         // com/monian/dubbo/provider/study/generic/SuperClass
  interfaces: 0, fields: 0, methods: 3, attributes: 2
Constant pool:
   #1 = Methodref          #5.#23         // com/monian/dubbo/provider/study/generic/SuperClass."<init>":()V
   #2 = Class              #24            // java/lang/String
   #3 = Methodref          #4.#25         // com/monian/dubbo/provider/study/generic/SubClass.get:(Ljava/lang/String;)Ljava/lang/String;
   #4 = Class              #26            // com/monian/dubbo/provider/study/generic/SubClass
   #5 = Class              #27            // com/monian/dubbo/provider/study/generic/SuperClass
   #6 = Utf8               <init>
   #7 = Utf8               ()V
   #8 = Utf8               Code
   #9 = Utf8               LineNumberTable
  #10 = Utf8               LocalVariableTable
  #11 = Utf8               this
  #12 = Utf8               Lcom/monian/dubbo/provider/study/generic/SubClass;
  #13 = Utf8               get
  #14 = Utf8               (Ljava/lang/String;)Ljava/lang/String;
  #15 = Utf8               s
  #16 = Utf8               Ljava/lang/String;
  #17 = Utf8               MethodParameters
  #18 = Utf8               (Ljava/lang/Object;)Ljava/lang/Object;
  #19 = Utf8               Signature
  #20 = Utf8               Lcom/monian/dubbo/provider/study/generic/SuperClass<Ljava/lang/String;>;
  #21 = Utf8               SourceFile
  #22 = Utf8               SubClass.java
  #23 = NameAndType        #6:#7          // "<init>":()V
  #24 = Utf8               java/lang/String
  #25 = NameAndType        #13:#14        // get:(Ljava/lang/String;)Ljava/lang/String;
  #26 = Utf8               com/monian/dubbo/provider/study/generic/SubClass
  #27 = Utf8               com/monian/dubbo/provider/study/generic/SuperClass
{
  public com.monian.dubbo.provider.study.generic.SubClass();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method com/monian/dubbo/provider/study/generic/SuperClass."<init>":()V
         4: return
      LineNumberTable:
        line 7: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/monian/dubbo/provider/study/generic/SubClass;

  public java.lang.String get(java.lang.String);
    descriptor: (Ljava/lang/String;)Ljava/lang/String;
    flags: (0x0001) 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/monian/dubbo/provider/study/generic/SubClass;
            0       2     1     s   Ljava/lang/String;
    MethodParameters:
      Name                           Flags
      s

  public java.lang.Object get(java.lang.Object);
    descriptor: (Ljava/lang/Object;)Ljava/lang/Object;
    flags: (0x1041) 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 get:(Ljava/lang/String;)Ljava/lang/String;
         8: areturn
      LineNumberTable:
        line 7: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  this   Lcom/monian/dubbo/provider/study/generic/SubClass;
    MethodParameters:
      Name                           Flags
      s                              synthetic
}
Signature: #20                          // Lcom/monian/dubbo/provider/study/generic/SuperClass<Ljava/lang/String;>;
SourceFile: "SubClass.java"

可以看到字节码中有两个get方法,第二个方法参数和返回值类型都是java.lang.Object 并且可以看到flags有相应标志ACC_BRIDGE, ACC_SYNTHETIC说明此方法就是有编译器自动生成的桥接方法。再看code属性:

aload_0:把this变量装载到操作数栈中

aload_1:把方法变量s装载到操作数栈中

checkcast # 2:校验栈顶变量s是否为java.lang.String类型

invokevirtual # 3: 调用方法 public String get(String s)

areturn: 返回结果 

根据上述code解释可以看出编译器生成的桥接方法为这个样子的,桥接方法实际上调用了实际的泛型方法

public String get(String s) {
 return s;
}

#桥接方法
public Object get(Object s) {
  return get((String) s);
}

泛型-类型擦除

public class SubClass extends SuperClass<String> {

  @Override
  public String get(String s) {
    return s;
  }

  public static void main(String[] args) {
    SuperClass subClass = new SubClass();
    Object s = "hello world";
    System.out.println(subClass.get(s));
  }
}

java的泛型在运行时会进行泛型擦除替换成非泛型上边界,java虚拟机无法知道准确的类型。 上述代码能编译通过并且会调用子类SubClass的桥接方法由桥接方法再去调用实际泛型方法。如果定义为SuperClassf7e83be87db5cd2d9a8a0b8117b38cd4 subClass = new SubClass();

So verwenden Sie die Java-Bridging-Methode

2 Eine Brückenmethode wird generiert, wenn

Wenn eine Unterklasse eine übergeordnete Klasse (geerbte Schnittstelle) erbt, um eine abstrakte generische Methode zu implementieren, generiert der Compiler automatisch eine Brückenmethode für die Unterklasse

{
  public com.monian.dubbo.provider.study.generic.SuperClass();
    descriptor: ()V
    flags: (0x0001) 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/monian/dubbo/provider/study/generic/SuperClass;
      LocalVariableTypeTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/monian/dubbo/provider/study/generic/SuperClass<TT;>;

  public abstract T get(T);
    descriptor: (Ljava/lang/Object;)Ljava/lang/Object;
    flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
    MethodParameters:
      Name                           Flags
      t
    Signature: #18                          // (TT;)TT;
}
# 🎜🎜# Verwenden Sie den Befehl javap -v SubClass.class, um den Bytecode der SubClass-Klasse anzuzeigen:

@Slf4j
public class SubClass extends SuperClass<String> {

  @Override
  public String get(String s) {
    return s;
  }

  public static void main(String[] args) throws Exception {

    SubClass subClass = new SubClass();
    Method bridgeMethod = subClass.getClass().getDeclaredMethod("get", Object.class);
    log.info("bridgeMethod is bridge:" + bridgeMethod.isBridge());
    log.info("bridgeMethod:" + bridgeMethod.toString());

    // 实际泛型方法
    Method actualMethod = subClass.getClass().getDeclaredMethod("get", String.class);
    log.info("actualMethod:" + actualMethod.toString());
    // 通过spring #BridgeMethodResolver由桥接方法获取到实际泛型方法
    Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(bridgeMethod);
    log.info("bridgedMethod:" + bridgedMethod.toString());
  }
}

Sie können sehen, dass der Bytecode zwei Get-Methoden enthält: Der zweite Methodenparameter und der Rückgabewerttyp sind beide java.lang.Object. Sie können sehen, dass die Flags die entsprechenden Flags ACC_BRIDGE und ACC_SYNTHETIC haben, was darauf hinweist, dass es sich bei dieser Methode um eine vom Compiler automatisch generierte Bridge-Methode handelt. Schauen Sie sich das Codeattribut noch einmal an:

aload_0: Laden Sie diese Variable in den Operandenstapel

aload_1: Laden Sie die Methodenvariable s in den Operandenstapel

#🎜 🎜 #checkcast # 2: Überprüfen Sie, ob die Variable s oben im Stapel vom Typ java.lang.String ist

So verwenden Sie die Java-Bridging-Methodeinvokevirtual # 3: Rufen Sie die Methode public auf String get(String s)

#🎜🎜#areturn : Ergebnis zurückgeben #🎜🎜##🎜🎜#Gemäß der obigen Codeerklärung ist ersichtlich, dass die vom Compiler generierte Bridge-Methode tatsächlich die eigentliche generische Methode #🎜 aufruft 🎜#rrreee#🎜🎜#Pan Type-type erasure#🎜🎜#rrreee#🎜🎜#Java-Generika werden zur Laufzeit generisch gelöscht und durch nicht generische Obergrenzen ersetzt, und die Java Virtual Machine kann den genauen Typ nicht kennen. Der obige Code kann kompiliert werden und ruft die Bridge-Methode der Unterklasse SubClass auf. Anschließend ruft die Bridge-Methode die eigentliche generische Methode auf. Wenn es als SuperClassf7e83be87db5cd2d9a8a0b8117b38cd4 subClass = new SubClass(); definiert ist, kann der Eingabeparameter der get-Methode nur eine String-Variable sein, da der Compiler während der Kompilierung eine Typprüfung durchführt Nicht konforme Typen werden direkt gemeldet. #🎜🎜##🎜🎜#3. Warum generische Methoden generieren? ) und Auf Bytecode-Ebene können wir sehen, dass nach der Kompilierung sowohl der Eingabe- als auch der Rückgabewerttyp der get-Methode Object sind. #🎜🎜##🎜🎜#Sie können sich vorstellen, dass die Kompilierung nicht erfolgreich ist, wenn keine vom Compiler automatisch generierte Überbrückungsmethode vorhanden ist. Nach der Kompilierung sind die Eingabeparameter der Unterklasse für die Get-Methode und der Rückgabewerttyp der Unterklasse Object, während die Eingabeparameter der Unterklasse für die Get-Methode und der Rückgabewerttyp String sind. Die Unterklasse überschreibt die Get-Methode der übergeordneten Klasse nicht (umschreiben: Zugriffsmethode). Der Implementierungsprozess muss neu geschrieben werden und der Rückgabewert und die formalen Parameter können nicht geändert werden. Alle Compiler müssen eine Brückenmethode generieren, und Object get(Object) kann kompiliert und übergeben werden. #🎜🎜##🎜🎜#4. Holen Sie sich die eigentliche generische Methode basierend auf der Bridge-Methode #🎜🎜##🎜🎜#Verwenden Sie hauptsächlich Springs BridgeMethodResolver#findBridgedMethod, um die überbrückte Methode zu finden von der Klasse und finden Sie Kandidatenmethoden mit demselben einfachen Namen und der gleichen Anzahl von Methodenparametern wie die Brückenmethode. Wenn es nur eine gibt, wird sie direkt zurückgegeben. Wenn es mehr als eine gibt, wird eine Schleife durchgeführt, um zu bestimmen, ob die Die Methodenparametertypen sind gleich oder die Kandidatenmethoden haben alle die gleiche Methodensignatur. Wählen Sie dann eine davon als Methode aus. Die überbrückte Methode. #🎜🎜#rrreee#🎜🎜#Die Ausgabe ist wie folgt: #🎜🎜##🎜🎜##🎜🎜##🎜🎜#

Das obige ist der detaillierte Inhalt vonSo verwenden Sie die Java-Bridging-Methode. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen