Ali, Baidu. Tencent boss comprende rápidamente los métodos y ejemplos de puentes de Java en tres minutos

Este artículo presenta principalmente el ejemplo del método puente para comprender rápidamente Java en 3 minutos. La introducción es muy detallada a través del código de muestra. Tiene un cierto valor de aprendizaje de referencia para el estudio o el trabajo de todos. Amigos que lo necesiten, sigan el editor a continuación. Aprende a aprender

¿Qué es el método puente?
El método Bridge en Java es un método que el compilador genera automáticamente para implementar ciertas características del lenguaje Java.

Podemos determinar si un método es un método puente a través del método isBridge de la clase Method.

En el archivo de código de bytes, el método puente se marcará como ACC_BRIDGE y ACC_SYNTHETIC, donde ACC_BRIDGE se usa para indicar que el método es un método puente generado por el compilador, y ACC_SYNTHETIC se usa para indicar que el método es generado automáticamente por el compilador.

¿Cuándo se genera el método puente?
¿Para lograr qué características del lenguaje Java generarán métodos puente? Las dos situaciones más comunes son el tipo de valor de retorno covariante y el borrado de tipo, porque dan lugar a inconsistencias entre los tipos de parámetros del método principal y el método real llamado. Entendamos mejor a través de dos ejemplos.

Tipo de valor devuelto covariante El tipo de valor devuelto
covariante significa que el tipo de valor devuelto del método de subclase no tiene que ser estrictamente equivalente al tipo de valor devuelto del método reemplazado en la clase principal, pero puede ser un tipo más "específico".

En Java 1.5, se agregó soporte para tipos de retorno covariantes, es decir, cuando una subclase anula un método de clase padre, el tipo devuelto puede ser una subclase del tipo de retorno del método de subclase. Veamos un ejemplo:

public class Parent {
    
    
  Number get() {
    
    
    return 1;
  }
}
public class Child extends Parent {
    
    
 
  @Override
  Integer get() {
    
    
    return 1;
  }
}

La clase Child anula el método get de su clase padre Parent. El tipo de retorno del método get del Parent es Number y el tipo de retorno del método get de la clase Child es Integer.

Compile este código y luego descompile:

javac Child.java
javap -v -c Child.class

Los resultados son los siguientes:

public class Child extends Parent
......省略部分结果......
  java.lang.Integer get();
    descriptor: ()Ljava/lang/Integer;
    flags:
    Code:
      stack=1, locals=1, args_size=1
         0: iconst_1
         1: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
         4: areturn
      LineNumberTable:
        line 5: 0
  java.lang.Number get();
    descriptor: ()Ljava/lang/Number;
    flags: ACC_BRIDGE, ACC_SYNTHETIC
    Code:
    
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokevirtual #3                  // Method get:()Ljava/lang/Integer;
         4: areturn
      LineNumberTable:
    

De los resultados anteriores, podemos ver que hay un método java.lang.Number get (), que no ha aparecido en el código fuente, y es generado automáticamente por el compilador. Este método está marcado como ACC_BRIDGE y ACC_SYNTHETIC, que es lo que hemos mencionado antes. Dicho método puente.

Este método juega un papel de puente, lo que hace es llamarse a sí mismo a través de la instrucción invokevirtual y luego llamar al método java.lang.Integer get ().

** ¿Cuál es la razón por la que el compilador hace esto? ** Porque en el método JVM, el tipo de retorno también es parte de la firma del método, y la firma del método puente es consistente con la firma del método de su clase padre, por lo que se realiza el tipo de valor de retorno covariante.

Los
genéricos de borrado de tipos son un concepto introducido solo en Java 1.5. Antes de esto, no existía el concepto de genéricos, pero el código genérico puede ser muy compatible con el código de versiones anteriores.

Esto se debe a que el compilador de Java reemplaza el parámetro de tipo con su límite superior (el tipo de la cláusula amplía en el parámetro de tipo) durante la compilación. Si el límite superior no está definido, toma el valor predeterminado de Object, que se denomina borrado de tipo.

Genere métodos de puente, por ejemplo:

public class Parent<T> {
    
    
 
  void set(T t) {
    
    
  }
}
public class Child extends Parent<String> {
    
    
 
  @Override
  void set(String str) {
    
    
  }
}

Cuando la clase Child hereda el método genérico de su clase principal Parent, especifica claramente el tipo genérico como String, compila este código y luego descompila:

public class Child extends Parent<java.lang.String>
......省略部分结果......
  void set(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags:
    Code:
      stack=0, locals=2, args_size=2
         0: return
      LineNumberTable:
        line 5: 0

  void set(java.lang.Object);
    descriptor: (Ljava/lang/Object;)V
    flags: 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 set:(Ljava/lang/String;)V
         8: return
      LineNumberTable
    ```
***从上面的结果可以看到,有一个方法void set(java.lang.Object), 在源码中是没有出现过的,是由编译器自动生成的,该方法被标记为ACC_BRIDGE和ACC_SYNTHETIC,就是我们前面所说的桥接方法。**

这个方法就起了一个桥接的作用,它所做的就是把对自身的调用通过invokevirtual指令再调用方法void set(java.lang.String)****编译器这么做的原因是什么呢?因为Parent类在类型擦除之后,变成这样:
```c
public class Parent<Object> {
    
    
 
  void set(Object t) {
    
    
  }
}

Para permitir que la subclase tenga un método que sea coherente con la firma del método de la clase principal, el compilador genera automáticamente un método puente en la subclase que sea coherente con la firma del método de la clase principal.

Cómo obtener el método real del método
puente La función de obtener el método real del método puente se ha implementado en Spring Framework, en la clase **** BridgeMethodResolver en el módulo spring-core, solo utilícelo directamente así :

method = BridgeMethodResolver.findBridgedMethod(method);

¿Cómo se implementa el método findBridgedMethod? Analicemos el código fuente (la versión de spring-core es 5.2.8.RELEASE):

public static Method findBridgedMethod(Method bridgeMethod) {
    
    
  // 如果不是桥连方法,就直接返回原方法。
 if (!bridgeMethod.isBridge()) {
    
    
 return bridgeMethod;
 }
  // 先从本地缓存读取,缓存中有则直接返回。
 Method bridgedMethod = cache.get(bridgeMethod);
 if (bridgedMethod == null) {
    
    
 List<Method> candidateMethods = new ArrayList<>();
    // 以方法名称和入参个数相等为筛选条件。
 MethodFilter filter = candidateMethod ->
  isBridgedCandidateFor(candidateMethod, bridgeMethod);
    // 递归该类及其所有父类上的所有方法,符合筛选条件就添加进来。
 ReflectionUtils.doWithMethods(bridgeMethod.getDeclaringClass()
      , candidateMethods::add, filter);
 if (!candidateMethods.isEmpty()) {
    
    
      // 如果符合筛选条件的方法个数为1,则直接采用;
      // 否则,调用searchCandidates方法再次筛选。
  bridgedMethod = candidateMethods.size() == 1 ?
   candidateMethods.get(0) :
   searchCandidates(candidateMethods, bridgeMethod);
 }
    // 如果找不到实际方法,则返回原来的桥连方法。
 if (bridgedMethod == null) {
    
    
  // A bridge method was passed in but we couldn't find the bridged method.
  // Let's proceed with the passed-in method and hope for the best...
  bridgedMethod = bridgeMethod;
 }
    // 把查找的结果放入内存缓存。
 cache.put(bridgeMethod, bridgedMethod);
 }
 return bridgedMethod;
}

Echemos un vistazo a cómo se implementa nuevamente el método de filtrado searchCandidates:

private static Method searchCandidates(List<Method> candidateMethods, Method bridgeMethod) {
    
    
 if (candidateMethods.isEmpty()) {
    
    
 return null;
 }
 Method previousMethod = null;
 boolean sameSig = true;
  // 遍历候选方法的列表
 for (Method candidateMethod : candidateMethods) {
    
    
    // 对比桥接方法的泛型类型参数和候选方法是否匹配,如果匹配则直接返回该候选方法。
 if (isBridgeMethodFor(bridgeMethod, candidateMethod, bridgeMethod.getDeclaringClass())) {
    
    
  return candidateMethod;
 }
 else if (previousMethod != null) {
    
    
      // 如果不匹配,则判断所有候选方法的参数列表是否相等。
  sameSig = sameSig && Arrays.equals(candidateMethod.getGenericParameterTypes()
        , previousMethod.getGenericParameterTypes());
 }
 previousMethod = candidateMethod;
 }
  // 如果所有候选方法的参数列表全相等,则返回第一个候选方法。
 return (sameSig ? candidateMethods.get(0) : null);
}

Resumir el código fuente anterior es obtener el método real del método puente juzgando el nombre del método, el número de parámetros y los parámetros de tipo genérico .

Este es el final de este artículo sobre cómo comprender rápidamente el método puente de Java en 3 minutos. Para obtener más contenido relacionado con el puente de Java, siga el editor o agregue wx1411943564 comentarios (csdn) para obtener más información.

Supongo que te gusta

Origin blog.csdn.net/dcj19980805/article/details/115283454
Recomendado
Clasificación