Rumah  >  Artikel  >  Java  >  Cara menggunakan kaedah penyambungan Java

Cara menggunakan kaedah penyambungan Java

王林
王林ke hadapan
2023-04-20 13:07:111050semak imbas

1. Pengenalan kepada kaedah penjembatan

Kaedah penjembatan digunakan oleh pengkompil untuk menjadikan kod bait yang dihasilkan oleh kaedah generik java serasi dengan kod bait sebelum jdk1.5 selepas pengenalan generik dalam jdk1. 5 Dijana secara automatik.

Anda boleh menggunakan method.isBridge() untuk menentukan sama ada kaedah itu adalah kaedah jambatan akan terdapat bendera bertanda ACC_BRIDGE, ACC_SYNTHETIC dalam kod bait yang dijana mengikut rajah bendera akses daripada Pemahaman Mendalam tentang Mesin Maya Java , anda boleh melihat perwakilan ACC_BRIDGE Kaedah adalah kaedah jambatan yang dijana oleh pengkompil ACC_SYNTHETIC menunjukkan bahawa kaedah dijana secara automatik oleh pengkompil dan bukan milik kod sumber.

Cara menggunakan kaedah penyambungan Java

2. Bilakah kaedah jambatan akan dijana

Apabila subkelas mewarisi kelas induk (antara muka diwarisi) untuk melaksanakan kaedah generik abstrak, pengkompil akan secara automatik menjana kaedah jambatan untuk subkelas

#父类
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;
  }
}

Gunakan perintah javap -v SubClass.class untuk melihat kod bait SubKelas kelas:

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"

Anda boleh melihat bahawa terdapat dua kaedah mendapatkan dalam bytecode , parameter kaedah kedua dan jenis nilai pulangan ialah java.lang.Object dan anda boleh melihat bahawa bendera mempunyai bendera yang sepadan ACC_BRIDGE dan ACC_SYNTHETIC, menunjukkan bahawa kaedah ini ialah kaedah jambatan yang dijana secara automatik oleh pengkompil. Lihat atribut kod sekali lagi:

aload_0: Muatkan pembolehubah ini ke dalam tindanan operan

aload_1: Muatkan pembolehubah kaedah s ke dalam tindanan operan

cast semak # 2: Sahkan sama ada pembolehubah s di bahagian atas tindanan adalah daripada jenis java.lang.String

invokevirtual # 3: Panggil kaedah public String get(String s)

return: Kembalikan hasil

Generik Java akan dipadamkan dan digantikan dengan sempadan atas bukan generik pada masa jalan dan mesin maya Java tidak dapat mengetahui jenis yang tepat. Kod di atas boleh disusun dan akan memanggil kaedah jambatan subkelas SubClass, dan kemudian kaedah jambatan akan memanggil kaedah generik sebenar. Jika ia ditakrifkan sebagai
public String get(String s) {
 return s;
}

#桥接方法
public Object get(Object s) {
  return get((String) s);
}
, maka parameter input kaedah get hanya boleh menjadi pembolehubah String, kerana pengkompil akan melakukan semakan jenis semasa penyusunan, dan jika jenis tidak sepadan dengan jenis, ia akan melaporkan kegagalan kompilasi secara langsung.

3. Mengapakah kaedah generik dijana
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));
  }
}

SuperClassf7e83be87db5cd2d9a8a0b8117b38cd4 subClass = new SubClass();Untuk menyusun dengan betul, anda boleh melihat bahawa jenis parameter kelas induk SuperClass get dalam kod sumber ialah T (T t), dan dalam sari kata Pada peringkat kod, anda boleh melihat bahawa selepas penyusunan, jenis input dan nilai pulangan kaedah get adalah kedua-duanya Objek.

Seperti yang anda boleh bayangkan, jika tiada kaedah penjembatan yang dijana secara automatik oleh pengkompil, maka kompilasi tidak akan lulus. Selepas penyusunan, SubClass kelas induk mendapatkan parameter input kaedah dan jenis nilai pulangan ialah Objek, manakala subkelas mendapatkan parameter input kaedah dan jenis nilai pulangan ialah String, subkelas tidak mengatasi kaedah get kelas induk (tulis semula: kaedah akses The proses pelaksanaan mesti ditulis semula, dan nilai pulangan serta parameter formal tidak boleh diubah). Semua penyusun perlu menjana kaedah jambatan, dan Object get(Object) boleh disusun dan diluluskan.

{
  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;
}
4. Dapatkan kaedah generik sebenar berdasarkan kaedah jambatan

Terutamanya gunakan Spring's BridgeMethodResolver#findBridgedMethod Prinsipnya ialah mencari dahulu semua kaedah yang diisytiharkan oleh kelas. Kaedah mencari dan merapatkan adalah mudah dengan nama dan bilangan parameter kaedah yang sama Jika terdapat hanya satu, ia akan dikembalikan secara langsung sama atau kaedah calon mempunyai tandatangan kaedah yang sama, dan kemudian mana-mana satu daripada kaedah tersebut akan dipilih sebagai kaedah tersusun.

Outputnya adalah seperti berikut:

@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());
  }
}

Atas ialah kandungan terperinci Cara menggunakan kaedah penyambungan Java. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam