Ali, Baidu. Tencent boss quickly understands Java bridge methods and examples in three minutes

This article mainly introduces the example of bridge method to quickly understand Java in 3 minutes. The introduction is very detailed through the sample code. It has a certain reference learning value for everyone's study or work. Friends who need it, follow the editor below. Learn to learn

What is the bridging method?
The Bridge Method in Java is a method that is automatically generated by the compiler in order to implement certain Java language features.

We can determine whether a method is a bridge method through the isBridge method of the Method class.

In the bytecode file, the bridge method will be marked as ACC_BRIDGE and ACC_SYNTHETIC, where ACC_BRIDGE is used to indicate that the method is a bridge method generated by the compiler, and ACC_SYNTHETIC is used to indicate that the method is automatically generated by the compiler.

When is the bridge method generated?
In order to achieve which Java language features will generate bridge methods? The two most common situations are the covariant return value type and type erasure, because they lead to inconsistencies between the parameter types of the parent method and the actual method called. Let's understand it better through two examples.

Covariant return type
Covariant return type means that the return value type of the subclass method does not have to be strictly equivalent to the return value type of the overridden method in the parent class, but can be a more "specific" type.

In Java 1.5, support for covariant return types was added, that is, when a subclass overrides a parent class method, the returned type can be a subclass of the return type of the subclass method. Let's look at an example:

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

The Child class overrides the get method of its parent class Parent. The return type of the get method of the Parent is Number, and the return type of the get method of the Child class is Integer.

Compile this code, and then decompile:

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

The results are as follows:

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:
    

From the above results, we can see that there is a method java.lang.Number get(), which has not appeared in the source code, and is automatically generated by the compiler. This method is marked as ACC_BRIDGE and ACC_SYNTHETIC, which is what we have previously Said bridging method.

This method plays a bridging role, what it does is to call itself through the invokevirtual instruction and then call the method java.lang.Integer get().

**What is the reason for the compiler to do this? ** Because in the JVM method, the return type is also part of the method signature, and the signature of the bridge method is consistent with the method signature of its parent class, so that the covariant return value type is realized.

Type erasure
Generics are a concept introduced only in Java 1.5. Before this, there was no concept of generics, but generic code can be well compatible with the code of previous versions. Why?

This is because the Java compiler replaces the type parameter with its upper bound (the type of the extends clause in the type parameter) during compilation. If the upper bound is not defined, it defaults to Object, which is called type erasure.

Generate bridging methods, for example:

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

When the Child class inherits the generic method of its parent class Parent, it clearly specifies the generic type as String, compile this code, and then decompile:

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) {
    
    
  }
}

In order to allow the subclass to have a method that is consistent with the method signature of the parent class, the compiler automatically generates a bridge method in the subclass that is consistent with the method signature of the parent class.

How to obtain the actual method of
the bridge method The function of obtaining the actual method of the bridge method has been implemented in the Spring Framework, in the ****BridgeMethodResolver class in the spring-core module, just use it directly like this:

method = BridgeMethodResolver.findBridgedMethod(method);

How is the findBridgedMethod method implemented? Let's analyze the source code (the version of spring-core is 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;
}

Let's take a look at how the searchCandidates method of filtering again is implemented:

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

Summarizing the above source code is to obtain the actual method of the bridge method by judging the method name, the number of parameters, and the generic type parameters .

This is the end of this article on how to quickly understand Java's bridge method in 3 minutes. For more related Java bridge content, please follow the editor or add wx1411943564 remarks (csdn) for more information!

Guess you like

Origin blog.csdn.net/dcj19980805/article/details/115283454