之前项目中有一段逻辑:接收消息、解析、校验、业务处理。一开始代码显得冗余,然后改成模板方法。发现不同消息体解析成不同的类。
public abstract class Tests {
public void handler(String message) {
InfoA infoA = parse(message);
try {
process(infoA);
} catch (Exception e) {
exceptionHandler(message, e);
}
}
private InfoA parse(String message) {
// 省略
return new InfoA();
}
abstract void process(InfoA infoA);
abstract void exceptionHandler(String message, Exception e);
}
此时是个具体类,为了消除类型,改成范型如下:
public abstract class Tests<T> {
public void handler(String message) {
T infoA = parse(message);
try {
process(infoA);
} catch (Exception e) {
exceptionHandler(message, e);
}
}
T parse(String message) {
// XXX 怎么写?
return null;
}
abstract void process(T infoA);
abstract void exceptionHandler(String message, Exception e);
}
最开始想用模板方法和范型的目的就是想减少代码冗余,因此使 parse(String message) 解析成相应的类即可。
这时候就需要 ParameterizedType 了,即参数化类型。查看源码解释如下:
/**
* ParameterizedType表示参数化的类型,例如
* Collection<String>。
* 参数化类型是在A第一次需要时创建的
* 反射方法,在此包中指定。当一个
* 创建参数化类型p,泛型类型声明
* 解析了p的实例化,并创建了p的所有类型参数
* 递归。看到{@link java.lang.reflect.TypeVariable
* TypeVariable}获取有关type创建过程的详细信息
* 变量。重复创建参数化类型没有效果。
* 一个equals()方法,它等于共享
* 相同的泛型类型声明,并具有相同的类型参数。
*
* @since 1.5
*/
也就是说范型进行了类型擦出,即Collection<String> 与 Collection<Integer> 在JVM中属于相同类型。但我们可以通过ParameterizedType 拿到容器里面的类型。
写法如下:
public abstract class Tests<T> {
public void handler(String message) {
T infoA = parse(message);
try {
process(infoA);
} catch (Exception e) {
exceptionHandler(message, e);
}
}
T parse(String message) {
Class<T> entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];// 注意这里是第0元素
return JsonUtils.fromJson(message, entityClass);
}
abstract void process(T infoA);
abstract void exceptionHandler(String message, Exception e);
}
可能有人要问为啥是第0元素,因为只范型里面只有一个。如果是 Tests<T, S> 的话,也可以继续取。