java的桥接方法和协变式返回类型

接上篇遇到的bug,总结以下在解决这个bug中遇到的其他问题和学到的新知识,在代码中使用的是ibatis提供的SqlMapClient,因此在setter中进行了new的转化,将demo部署到服务器上之后,启动jboss服务器,服务器无法正常启动,报错主要内容如下:
Bean property 'mysqlReaderMapClient' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
以下是关于这个bug找到的一些资料:
https://jira.springsource.org/browse/SPR-5024
https://jira.springsource.org/browse/SPR-2727
https://jira.springsource.org/browse/SPR-5254
开始因为我们对这个问题并没有一个比较直观的认识与理解,只能走一步看一步,其中仔细看了一些资料,编写了一些测试代码,将主要学习到的知识介绍如下:
第一篇的主题是:Introspection problem: setter not found when corresponding getter uses a covariant return type,第二篇的主题是:Dealing with covariant properties in CachedIntrospectionResult,通过阅读这两篇,主要新接触了两个概念,第一个是“java桥接方法”、“协变返回类型”,将这两个对我而言比较新的名词和大家分享一下:
一、 Java Bridge Method
主要参考了以下两篇感觉比较好的blog:
http://jlife.iteye.com/blog/638407
http://berdy.iteye.com/blog/810488
下面讲一下自己在实践中的一些想法吧,桥接主要是为了兼容jdk 1.4采取的一种措施,桥接方法解决的问题是jdk1.5开始支持泛型,关于泛型的概念,常见的比如:
List<String> strList = new ArrayList<String>();
这就是对泛型比较简单的使用,泛型的好处让从集合中取数据的程序员能够知道自己取的是什么,稍微复杂一些的泛型的应用如下:
public abstract class A<T>{
abstract T get(T t);
}
public class B extends A<String>{
String get(String t);
}
通过泛型指定B中的get返回的类型必须为指定的类型或者子类,其本质就是将原来要在运行期间的检查工作提前到了编译阶段,如果返回的类型不是指定的类型的话,就要报错,为了满足这个特性,同时要能够在老的版本中运行,首先用一个简单的例子去说明这个问题:
在父类中该T会被转换成Object:
public abstract class A<Object>{
abstract Object get(Object  obj);
}
而子类则是:
public class B{
String get(String str);
}
这样在编译时肯定会报错,原因是B继承自A,而A是abstract class,而B并没有overriding掉A中的Object get(Object obj),为了解决这个问题,编译器做了一些工作,当发现我们指定类T的类型时,编译器会在字节码中添加一个桥接方法来overriding掉父类的方法,可以通过查看编译器的字节码可以发现,也可以通过反射来查看类中的方法:
在已有的A.java和B.java的基础上我们添加一个测试类BridgeTest.java,代码如下:
import java.lang.reflect.Method;
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 getjava.lang.String  is Bridge Method? false
class java.lang.Object getjava.lang.Object  is Bridge Method? True
通过分析结果,get返回String的方法不是桥接的,而返回Object是桥接的,这就是编译器做的一些工作。
二、 Covariant Return Type
翻译成中文是协变式返回类型,在介绍这个概念之前先介绍下jdk1.4和jdk1.5中关于函数签名的定义:
In Java 1.4, and earlier, one method can override another if the signatures match exactly.
In Java 5, a method can override another if the arguments match exactly but the return type of the overriding method, if it is a subtype of the return type of the other method.
也就是说,在jdk1.5中,对于“重写”的判断放宽了条件,子类中方法返回的类型是父类中方法返回类型的子类也算是重写。
可以用下面一个简单的例子来说明:
public class A{
Father get();
}
public class B{
Son get();
}
其中class Son extends Father,这就是协变式返回类型。

你可能感兴趣的:(java,应用服务器,bean,jboss,ibatis)