昨天利用泛型写了个泛型工具类,这个类使用反射能够得到泛型参数,比如有一个泛型为Type<Student>,那么我们使用这个工具类就能得到Student这个类的实例对象,代码如下:
public abstract class TypeClass<T> {
/**
* 返回给定子类类型的直接父类的参数类型。
*
* @param subclass 给定的子类。
* @return 给定子类类型的直接父类的参数类型。
*/
@SuppressWarnings("unchecked")
static Type getSuperclassTypeParameter(Class<?> subclass) {
Type superclass = subclass.getGenericSuperclass();
if (superclass instanceof Class) {
throw new RuntimeException("缺少类型参数!");
}
return ((ParameterizedType) superclass).getActualTypeArguments()[0];
}
}
这时我们自己写一个类继承此泛型类,并且以一个javabean或一个entity来作为此泛型类的参数,那么我们最后就能得到这个javabean或entity,然后我们就可以对javabean或entity进行具体的操作。
如果我们给了具体的类型,要得到此给定类型的原始类型或接口时,我们就要使用ParameterizedType类的getRawType()方法了,如:
/* 返回给定类型的原始类型或接口。 */
static Class<?> getRawType(Type type) {
if (type instanceof Class<?>) {
// type 是一个普通的类
return (Class<?>) type;
} else if (type instanceof ParameterizedType) { // 参数化类型
ParameterizedType paramType = (ParameterizedType) type;
Type rawType = paramType.getRawType();
if (rawType instanceof Class<?>) return (Class<?>) rawType;
throw buildUnexpectedTypeError(rawType, Class.class);
} else if (type instanceof GenericArrayType) { // 数组类型
GenericArrayType genericArrayType = (GenericArrayType) type;
Object rawArrayType = Array.newInstance(getRawType(genericArrayType
.getGenericComponentType()), 0);
return rawArrayType.getClass();
} else {
throw unexpectedTypeError(type, ParameterizedType.class, GenericArrayType.class);
}
}
unexpectedTypeError是一个失败断言类:
/* 构造无法判断类型的断言失败对象。 */
private static AssertionError unexpectedTypeError(Type t, Class<?>... expected) {
StringBuilder buffer = new StringBuilder("无法预知的类型。现可知的类型有 ");
for (Class<?> c : expected) {
buffer.append(c.getName()).append(", ");
}
buffer.append("但是得到 ").append(t.getClass().getName());
buffer.append(", 类型表示的是:").append(t.toString());
return new AssertionError(buffer.toString());
}
我们得在此泛型类当中加上两个构造函数:
/* 原始类型。 */
final Class<? super T> rawType;
/* 该泛型类型要构造的类型。 */
final Type type;
/**
* 构造一个新的 {@code Type} 类型对象。
*/
@SuppressWarnings("unchecked")
protected TypeClass() {
type = getSuperclassTypeParameter(getClass());
rawType = (Class<? super T>) getRawType(type);
}
/**
* 构造一个给定类型的 {@code TypeClass} 对象。
*
* @param type 给定的 {@code Type} 类型。
*/
@SuppressWarnings("unchecked")
protected TypeClass(Type type) {
this.rawType = (Class<? super T>) getRawType(nonNull(type, "type"));
this.type = type;
}
/* 检查泛型类型是否为空 */
static <T> T nonNull(T o, String message) {
if (o == null) {
throw new NullPointerException(message);
}
return o;
}
这样之后我们基本上已经可以实现泛型反射的作用了,但是我们还需要考虑一下之后如果继承此类都需要在相应的方法上进行强制转换类型的步骤,所以我们可以在此泛型类当中写一个内部类来继承此泛型类:
public static TypeClass<?> get(Type type) {
return new SimpleTypeClass<Object>(type);
}
public static <T> TypeClass<T> get(Class<T> type) {
return new SimpleTypeClass<T>(type);
}
private static class SimpleTypeClass<T> extends TypeClass<T> {
public SimpleTypeClass(Type type) {
super(type);
}
}
这样以后我们在具体的操作当中就只需要调用相应的get方法就行了,其他工作就交给TypeClass来做了。
由上面这个类我们可以联想到切面编程,比如有一个抽象的JpaDaoImpl.java类,所以继承该类的XXXJpaDAOImpl,都对应于一个数据库表(table)。<E extends AbstractEntity, I>中的‘E’就表示该数据库表对应于Java中的entity。JpaDaoImpl类本身也implements了Dao<E, I>的接口。Dao<E, I>定义了JpaDaoImpl所需要实现的基本方法(基于数据库表的CUID方法)。
通过“(Class<E>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]”,就可以得到E在实际的XXXJpaDaoImpl.java类中是什么具体的entity。得到E,我们就可以通过hibernate实现基本的CUID操作了。
JpaDaoImpl.java类的代码如下:
/**
*
* JPA implementation of DAO.
*
* @param <E> entity type
* @param <I> primary key type
*/
public class JpaDaoImpl<E extends AbstractEntity, I> extends JpaDaoSupport implements Dao<E, I> {
protected Class<E> entityClass;
@SuppressWarnings("unchecked")
public JpaDaoImpl() {//very tricky here
entityClass = (Class<E>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
public E create(E entity) {
getJpaTemplate().persist(entity);
return entity;
}
public void delete(E entity) {
getJpaTemplate().remove(entity);
}
public void delete(I id) {
getJpaTemplate().remove((find(id)));
}
public boolean exists(I id) {
return find(id) != null;
}
public E find(I id) {//entityClass is used
return getJpaTemplate().find(entityClass, id);
}
public E update(E entity) {
return getJpaTemplate().merge(entity);
}
public List<E> query() {//entityClass is used
return query("from " + entityClass.getSimpleName());
}
......
}
Dao<E, I>接口的定义如下:
public interface Dao<E extends AbstractEntity, I> {
E create(E entity);
E update(E entity);
void delete(E entity);
void delete(I id);
E find(I id);
boolean exists(I id);
......
}
继承JpaDaoImpl.java类的两个具体实现,分别为User表和Privilege表提供基本的CUID操作。public class UserDaoImpl extends JpaDaoImpl<User, Long>{
//No method needs
}
public class PrivilegeDaoImpl extends JpaDaoImpl<Privilege, String>{
//No method needs
}