在搭建项目的时候,我们经常会用到如下代码,用于抽取出通用的方法放在父类中。
public abstract class BaseDaoImpl<T> implements BaseDao<T> {
....
protected Class getActualType(){
Type theType = this.getClass().getGenericSuperClass();//获得泛型父类
if (theType instanceof ParameterizedType) {
return (Class)((ParameterizedType)theType).getActualTypeArguments()[0];
} else {
return Object.class;
}
}
protected T findById(Integer id) {
return (T)this.getSession().get(getActualType(),id);
//这样这个方法就可以被通用
}
}
然后再BaseDaoImpl的子类中,比如UserDaoImpl,就直接拥有了findById的方法
public class UserDaoImpl extends BaseDaoImpl<User> implements UserDao {
........
}
在BaseDaoImpl的实现类中,我们无法拿到T的具体类型,因为它是个泛型,不可能通过T.class取得我们想要的结果。所以,我们可以在UserDaoImpl调用findById方法的时候,获得泛型父类(BaseDaoImpl<User>),再拿到泛型参数,即可得到User.class。
这里的实现最关键的方法是Type getGenericSuperClass(),如果当前class的父类使用了泛型编程,即是泛型,则返回父类,否则返回Object.class.
我们写一些简单的方法对如上的代码作些解析。
javaGrammar.java是泛型的。
public class JavaGrammar<T> {
public static void mian(String args[]) {
Type type1 = JavaGrammar.class.getGenericSuperclass();
Type type2 = SubJavaGrammar.class.getGenericSuperclass();
System.out.println(type1);//输出class java.lang.Object
System.out.println(type2);//输出com.xiao.client.JavaGrammar<com.xiao.client.Counter>
if (type1 instanceof ParameterizedType) {
System.out.println(((ParameterizedType)type1).getActualTypeArguments()[0]);//没有输出
}
if (type2 instanceof ParameterizedType) {
//如果SubJavaGrammar的父类JavaGrammar是泛型的,我们就可以得到父类中泛型的实际类
System.out.println(((ParameterizedType)type2).getActualTypeArguments()[0]);//class com.xiao.client.Counter
System.out.println(((ParameterizedType)type2).getRawType());//class com.xiao.client.JavaGrammar
System.out.println(((ParameterizedType)type2).getOwnerType());//null
}
}
}
它有一个子类SubJavaGrammar.java
public class SubJavaGrammar extends JavaGrammar<Counter> {
}
Couter.类只是用来明确指定一个Type,即T的类型。
public class Counter {
//This is a empty class
}
当然,子类要使用父类的泛型特征,才能得到我们想要的结果。
我们再来看如下代码:
public class SubJavaGrammar extends JavaGrammar {
}
如果SubJavaGrammar是如上情况的话,System.out.println(type2);//输出class com.xiao.client.JavaGrammar
也就是说:实际泛型参数是在执行的时候传过去的。如果在实际执行的时候,并没有将实际类型传过去,那么即便JavaGrammar中写成了
public class JavaGrammar<Counter> {
....
}
我们也是没法拿到Counter类的。这时,type1和type2都不是ParameterizedType的实例。
附注:
如果要满足抽取通用方法的需求,我们也可以在不使用泛型的情况下实现。思路是采用模板方法,让子类返回实际类。
public abstract class JavaGrammar {
protected abstract Class getClazz();//获取实体类型的工作交给子类去做
....
public Object findById(Integer id) {
return this.getSession().get(getClazz(),id);
}
}
在JavaGrammar的子类SubJavaGrammar
public class SubJavaGrammar extends JavaGrammar {
public Class getClazz() {
return User.class;
}
......
}
这样SubJavaGrammar也能直接调用父类的getById方法。只不过还需要自己将Object对象转换为User。