Javaブリッジングメソッドの使用方法

王林
リリース: 2023-04-20 13:07:11
転載
1066 人が閲覧しました

1. ブリッジメソッドの紹介

ブリッジメソッドは、jdk1.5 でジェネリックスが導入された後のメソッドであり、Java ジェネリックメソッドで生成されたバイトコードを jdk1.5 以前のバイトコードと互換性を持たせるために使用されます。バージョン、コンパイラが自動的に生成されます。

Availablemethod.isBridge()メソッドがブリッジ メソッドであるかどうかを判断するには、詳細な理解の図によると、生成されたバイトコードに ACC_BRIDGE、ACC_SYNTHETIC とマークされたフラグが存在します。アクセスフラグ図から、ACC_BRIDGE表現方法はコンパイラによって生成されるブリッジメソッドであり、ACC_SYNTHETIC表現方法はコンパイラによって自動生成され、ソースコードには属さないことがわかります。

Javaブリッジングメソッドの使用方法

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 メソッドが 2 つあり、2 番目のメソッドのパラメータと戻り値の型は両方とも java.lang.Object であり、フラグに対応するフラグ ACC_BRIDGE および ACC_SYNTHETIC があることがわかり、このメソッドがコンパイラによって自動的に生成されたブリッジ メソッドであることがわかります。コード属性をもう一度見てください:

aload_0: この変数をオペランド スタックにロードします

aload_1: メソッド変数 s をオペランド スタックにロードします

checkcast # 2: 検証しますスタックの一番上の変数 s が java.lang.String

invokevirtual 型かどうか # 3: メソッド public String get(String s)

areturn を呼び出します: 結果を返します

上記のコードの説明によると、コンパイラによって生成されたブリッジ メソッドは次のようになっていることがわかります。ブリッジ メソッドは実際に実際のジェネリック メソッド

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

#桥接方法
public Object get(Object s) {
  return get((String) s);
}
ログイン後にコピー

Generics-type Erasure## を呼び出します。 #

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 のブリッジ メソッドが呼び出され、実際のジェネリック メソッドが呼び出されます。

SuperClass subClass = new SubClass();として定義されている場合、コンパイラはコンパイル中に型チェックを実行するため、get メソッドの入力パラメータは String 変数のみになります。コンパイラの失敗が直接報告されます。

3. ジェネリック メソッドを生成する理由

{
  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;
}
ログイン後にコピー

正しくコンパイルするには、ソース コード内の親クラス SuperClass get メソッドのパラメーターの型が T (T t) であることがわかります。バイトコード内では、コンパイル後のレベルで、get メソッドの入力および戻り値の型が両方とも Object であることがわかります。

コンパイラによって自動的に生成されたブリッジング メソッドがなければ、コンパイルは成功しないことが想像できます。コンパイル後、親クラスの SubClass の get メソッドの入力パラメータと戻り値の型は Object になりますが、サブクラスの get メソッドの入力パラメータと戻り値の型は String になります。サブクラスは親クラスの get メソッドをオーバーライドしません (書き換え: アクセス メソッド実装処理を書き直す必要があり、戻り値や仮パラメータは変更できません)。すべてのコンパイラはブリッジ メソッドを生成する必要があり、Object get(Object) をコンパイルして渡すことができます。

4. ブリッジ メソッドに基づいて実際のジェネリック メソッドを取得する

主に Spring の BridgeMethodResolver#findBridgedMethod を使用してブリッジ メソッドを検索します。原則として、最初にクラスによって宣言されているすべてのメソッドを検索します。メソッドの検索とブリッジは簡単です。同じ名前とメソッド パラメータの数を持つメソッドの候補です。1 つしかない場合は、直接返されます。複数ある場合は、ループしてメソッド パラメータのタイプが正しいかどうかを判断します。同じであるか、候補メソッドが同じメソッド シグネチャを持っている場合、それらのいずれか 1 つがブリッジ メソッドとして選択されます。

@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());
  }
}
ログイン後にコピー
出力は次のとおりです:

Javaブリッジングメソッドの使用方法

以上がJavaブリッジングメソッドの使用方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:yisu.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート