Type 是 java 类型信息体系中的顶级接口,其中 Class 就是 Type 的一个直接实现类。此外,Type 还有有四个直接子接口:ParameterizedType,TypeVariable,WildcardType,GenericArrayType。
这几个接口的介绍如下:
- Type
它是所有类型的公共接口。包括原始类型、参数化类型、数组类型、类型变量和基本类型。ParameterizedType, TypeVariable, WildcardType,GenericArrayType 这四个接口都是它的子接口。
- GenericDeclaration
这个接口 Class、Method、Constructor 都有实现,我们就是要用这个接口的 getTypeParameters 方法,它返回一个 TypeVariable[]数组,这个数组里面就是我们定义的类型变量 T 和 K,顺序与我们声明时一样。如果用循环语句将数组打印出来,你会发现只会输出 T 和 K,这可不是我们想要的结果,那么想要获得预期的结果怎么办呢?请继续往下看。
- TypeVariable
它表示类型变量。比如 T,比如 K extends Comparable<? super T> & Serializable,这个接口里面有个 getBounds()方法,它用来获得类型变量上限的 Type 数组,如果没有定义上限,则默认设定上限为 Object,请注意 TypeVariable 是接口,实际得到的是 TypeVariableImpl 实现类,下面几个接口都一样。
拿 T 和 K 来说明,T 没有定义任何上限,所以它就有一个默认上限 java.lang.Object,实际跟踪代码的时候你会发现 T 的 bounds 属性为空,只有在调用了 getBounds()方法后,才会有一个 Type[1]数组[class java.lang.Object]。而对于 K 来说,调用了 getBounds 方法后,得到的数组是[java.lang.Comparable<? super T>, interface java.io.Serializable],它们的类型却是不一样的,第 1 个是 ParameterizedType,而第二个是 Class.
- ParameterizedType
ParameterizedType 表示参数化类型,就是上面说的 java.lang.Comparable<? super T>,再比如 List,List,这些都叫参数化类型。得到 Comparable<? super T> 之后,再调用 getRawType()与 getActualTypeArguments()两个方法,就可以得到声明此参数化类型的类(java.lang.Comparable)和实际的类型参数数组([? super T]),而这个? super T 又是一个 WildcardType 类型。
- WildcardType
它用来描述通配符表达式,上面返回的? super T 正好是这个类型。然后调用 getUpperBounds()上限和 getLowerBounds()下限这两个方法,获得类型变量?的限定类型(上下限),对于本例的通配符(?),它的上限为 java.lang.Object,下限为 T
通过上面几个接口的分析,可以将 Person 类的泛型参数都解析出来,那么 Person 的超类以及实现的接口该怎么处理呢?Class 类里面同样在 1.5 版本加入了 getGenericSuperclass()与 getGenericInterfaces()两个方法,用于返回带参数化类型的超类与接口。
GenericArrayType 其实就是泛型数组类型。
我们说 Class 在一定程度上挽救了擦除的类型信息,我们就可以通过这几个接口来获取被擦除的类型参数信息,这几个接口无非就是对类型参数的一个分类,通过它们提供的一些方法,我们可以逐步的获取到最原始的类型参数的 Class 对象。
具体的说明和 API 大家可以去看文档,我这里记录一个实际的应用,当然在各种框架中的应用比比皆是。
在 JavaEE 的 Dao 层我们一般都会封装出一个通用的泛型 BaseDao,它可以实现对各种实体例如 User,Order 的基本 CRUD,然后具体的 UserDao,OrderDao 等等会去继承它,提供其他的 Dao 方法:
public class UserDao extends BaseDao<User>{}
我们使用的 BaseDao 是基于 DBUtils 的,它需要实体的 Class 对象才能进行通用的查询方法,例如 User 的 Class 对象,我们可以通过构造函数,函数参数等手段传递给 BaseDao,但是有了反射,可以有更优雅的实现。
public class BaseDao<T> {
private Class<T> clszz;
public BaseDao(){
Type type = this.getClass().getGenericSuperclass();//拿到带类型参数的泛型父类
if(type instanceof ParameterizedType){//这个Type对象根据泛型声明,就有可能是4中接口之一,如果它是BaseDao<User>这种形式
ParameterizedType parameterizedType = (ParameterizedType) type;
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();//获取泛型的类型参数数组
if(actualTypeArguments != null && actualTypeArguments.length == 1){
if(actualTypeArguments[0] instanceof Class){//类型参数也有可能不是Class类型
this.clszz = (Class<T>) actualTypeArguments[0];
}else{
//例如: BaseDao<BaseDao<User>>,获取到的就不是Class,而又是ParameterizedType,即嵌套的
ParameterizedType,一层一层剥开,最终是可以得到User的Class对象的
}
}
}
}
public T get(String sql,Object...params){
QueryRunner qr = new QueryRunner();
T obj;
Connection connection;
try {
connection = JdbcUtil.getConnection();
obj = qr.query(connection,sql,new BeanHandler<T>(clszz),params);
} catch (SQLException e) {
e.printStackTrace();
return null;
}
return obj;
}
}
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于