Bridge Method (桥接方法)

在 @readThroughSingleCache 的实现 中有一个方法:

final Method methodToCache = getCacheBase().getMethodToCache(pjp);

作用是获取需要缓存的方法。

具体如下:

public Method getMethodToCache(final JoinPoint jp) throws NoSuchMethodException {
        final Signature sig = jp.getSignature();
        if (!(sig instanceof MethodSignature)) {
            throw new InvalidAnnotationException("This annotation is only valid on a method.");
        }

        final MethodSignature msig = (MethodSignature) sig;
        final Object target = jp.getTarget();

        // cannot use msig.getMethod() because it can return the method where annotation was declared i.e. method in
        // interface

        // 得到方法的名称和他的参数
        String name = msig.getName();
        Class<?>[] parameters = msig.getParameterTypes();

        Method method = findMethodFromTargetGivenNameAndParams(target, name, parameters);

        if (method.isBridge()) {
            if (getLogger().isDebugEnabled()) {
                getLogger().debug("Method is bridge. Name {}, params: {}", name, parameters);
            }

            parameters = bridgeMethodMappingStore.getTargetParamsTypes(target.getClass(), name, parameters);
            method = findMethodFromTargetGivenNameAndParams(target, name, parameters);
        }

        return method;
    }

这正好就用到了桥接方法。

在jdk 1.5之前,还没有泛型,

创建了一个集合对象,这时候什么样子的数据元素都可以放进去,用的时候再强制转换类型。编译器在编译期间是可以通过的,但是如果在运行时,转换类型不容许,那是会出错的。

那在JDK 1.5 之后引入了泛型。 使用者可以定义集合的类型。这时候就必须是自己定义的类型集合,你定义的String类型的,而你又想加入Integer的元素是不容许的。这样的话,就在编译器编译检查这钟情况了。

问题来了,如何兼容JDK 1.5 前后这种情况呢? 就是说编译器现在编译期间都提示这种错误了

举例说明:

    abstract class A<T> {  
    abstract T get(T t);  
    }  
    
    class B extends A<String> {  
    @Override
        String get(String s) {  
    return "";  
        }  
    }  
    
    public class TestBridge {  
    public static void main(String[] args) {  
            Class<B> clazz = B.class;  
            Method[] methods = clazz.getDeclaredMethods();  
    for (int i = 0; i < methods.length; i++) {  
                Method m = methods[i];  
                System.out.println(getMethodInfo(m) + " is Bridge Method? " + m.isBridge());  
            }  
        }  
    
    public static String getMethodInfo(Method m){  
            StringBuilder sb = new StringBuilder();  
            sb.append(m.getReturnType()).append(" ");  
            sb.append(m.getName());  
            Class[]params = m.getParameterTypes();  
    for (int i = 0; i < params.length; i++) {  
                sb.append(params[i].getName()).append(" ");  
            }  
    return sb.toString();  
        }  
    }

运行这段代码,可以看到的结果:

[class java.lang.String get] is Bridge Method? false 
[class java.lang.Object get] is Bridge Method? true

奇怪吧,有两个方法了,明明我只写了一个方法啊!!

下面来说说java5编译器是如何来编译上面的代码:

Java代码  

abstract class A<T> {  
abstract T get(T t);  
}

对于A类,编译器看到<>中指定的T参数后,会用Object把类中的其他T参数替换。因为在jdk中根本就不存在T这个类嘛。替换后就成了下面这样子。

Java代码  

abstract class A {  
abstract Object get(Object obj);  
}

上面这个过程称为类型擦除。对于B类,它继承了A类,指定了T参数为String。如果还按照以前那么编译,那编译的类就是:

Java代码  

class B extends A {  
    String get(String s) {  
return "";  
    }  
}

这样在运行时肯定会报错,因为B继承了A,而A又是asbtract类,B还没overriding A中Object get()方法。如何解决这个错误呢?java5编译器在编译的时候做了些手脚。当编译器发现你指定了类型参数,便会在编译的字节码中添加一个桥接方法。这个可以查看B的反编译代码就知道了。

Java代码  

class B extends A {  
//编译器添加的方法
    Object get(Object s) {  
return (Object) get((String) s);  
    }  

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

对了,就是 在B类中编辑器给新增了一个方法。

桥接方法: 就是连接两个东西的方法。

这个方法可以用于为了升级所带来的不兼容问题。

原文地址:https://www.cnblogs.com/pan2011/p/3650318.html