如果要动态构造一个泛型参数对象(ParameterizedType),guava提供的TypeToken工具可以部分实现这个功能:
比如下面这个例子(来自guava wiki TypeToken),可以根据需要定制一个指定K,V类型的Map。
static TypeToken
但是guava提供的这个方法只能只能根据TypeParameter构造一个新的ParameterizedType,如果想根据一个已有的ParameterizedType对象替换其中的参数,上面的方法并不能实现。
比如,已经有一个Map
类型(也可能是HashMap,LinkedMap,Hashtable,…),现在希望将它的value type改为java.util.Date ,构造一个Map
类型。
其实既然TypeToken工具能根据TypeParameter构造一个新的ParameterizedType,实现上面这个需求并不复杂,不知道为什么guava没有提供这个方法(我用的版本是16)。
实现这个需求最关键的就是要有一个ParameterizedType接口的实现类,有了这个实现类,你想怎么替换都成。
可惜,不论是jdk还是guava的 ParameterizedTypeImpl类都不是public的,所以没办法直接拿来用。所以就只能自己造一个。
虽然 ParameterizedType接口方法也没几个,但如何自己写个ParameterizedTypeImpl呢?
别逗了,还真打算从头自己写一个啊,再说自己的写的敢用么?
直接把jdk中的ParameterizedTypeImpl代码抄来改改就可以啦
其实这个问题我也是琢磨了好长时间才想通的。
于是我把sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
的代码几乎是原封不动的抄来,在此基础上根据需要增加了构造方法和transform方法实现了参数类型替换。完整代码如下(中文注释部分是我增加的方法),代码中用到了guava中的TypeToken工具类实现,只是为了少写些代码。
ParameterizedTypeImpl.java
package gu.rpc.thrift;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.lang.reflect.MalformedParameterizedTypeException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;
import com.google.common.reflect.TypeToken;
/** * 基于jdk1.7中 {@link sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl}实现 * @author guyadong * */
@SuppressWarnings("restriction")
class ParameterizedTypeImpl implements ParameterizedType {
private Type[] actualTypeArguments;
private Class> rawType;
private Type ownerType;
/** * 构造方法 * 基于已有{@link ParameterizedType}实例构造一个新对象 * @param source 不可为{@link null} */
ParameterizedTypeImpl(ParameterizedType source){
this(TypeToken.of(checkNotNull(source)).getRawType(),source.getActualTypeArguments(),source.getOwnerType());
}
ParameterizedTypeImpl(Class> rawType, Type[] actualTypeArguments, Type ownerType) {
this.actualTypeArguments = actualTypeArguments;
this.rawType = rawType;
this.ownerType = ownerType != null ? ownerType : rawType.getDeclaringClass();
this.validateConstructorArguments();
}
private void validateConstructorArguments() {
TypeVariable>[] formals = this.rawType.getTypeParameters();
if (formals.length != this.actualTypeArguments.length) {
throw new MalformedParameterizedTypeException();
}
for (int i = 0; i < this.actualTypeArguments.length; ++i) {
}
}
@Override
public Type[] getActualTypeArguments() {
return (Type[]) this.actualTypeArguments.clone();
}
@Override
public Class> getRawType() {
return this.rawType;
}
@Override
public Type getOwnerType() {
return this.ownerType;
}
public boolean equals(Object o) {
if (o instanceof ParameterizedType) {
ParameterizedType that = (ParameterizedType) o;
if (this == that) {
return true;
}
Type thatOwner = that.getOwnerType();
Type thatRawType = that.getRawType();
return (this.ownerType == null ? thatOwner == null : this.ownerType.equals(thatOwner))
&& (this.rawType == null ? thatRawType == null : this.rawType.equals(thatRawType))
&& Arrays.equals(this.actualTypeArguments, that.getActualTypeArguments());
}
return false;
}
public int hashCode() {
return Arrays.hashCode(this.actualTypeArguments) ^ (this.ownerType == null ? 0 : this.ownerType.hashCode())
^ (this.rawType == null ? 0 : this.rawType.hashCode());
}
public String toString() {
StringBuilder sb = new StringBuilder();
if (this.ownerType != null) {
if (this.ownerType instanceof Class) {
sb.append(((Class) this.ownerType).getName());
} else {
sb.append(this.ownerType.toString());
}
sb.append(".");
if (this.ownerType instanceof ParameterizedTypeImpl) {
sb.append(this.rawType.getName()
.replace(((ParameterizedTypeImpl) this.ownerType).rawType.getName() + "$", ""));
} else {
sb.append(this.rawType.getName());
}
} else {
sb.append(this.rawType.getName());
}
if (this.actualTypeArguments != null && this.actualTypeArguments.length > 0) {
sb.append("<");
boolean first = true;
for (Type t : this.actualTypeArguments) {
if (!first) {
sb.append(", ");
}
if (t instanceof Class) {
sb.append(((Class) t).getName());
} else {
sb.append(t.toString());
}
first = false;
}
sb.append(">");
}
return sb.toString();
}
/** * 将当前对象的类型参数中为{@code oldType}的元素替换为{@code newType} * @param oldType 不可为{@code null} * @param newType 不可为{@code null} * @return */
public ParameterizedType transform(Type oldType,Type newType ){
checkNotNull(oldType);
checkNotNull(newType);
Type[] typeArgs = getActualTypeArguments();
for(int i =0 ;iif(typeArgs[i]==oldType)
typeArgs[i] = newType;
}
return new ParameterizedTypeImpl(TypeToken.of(this).getRawType(), typeArgs, getOwnerType());
}
/** * 用指定的类型参数替换当前对象的类型参数
* 新参数的个数与当前对象的类型参数个数必须一致, * 如果新参数数组中元素为{@code null}则对应的参数不会被替换 * @param newTypeArguments * @return */
public ParameterizedType transform(Type[] newTypeArguments){
checkNotNull(newTypeArguments);
Type[] typeArgs = getActualTypeArguments();
checkArgument(newTypeArguments.length == typeArgs.length );
for(int i=0;iif(null != newTypeArguments[i]){
typeArgs[i] = newTypeArguments[i];
}
}
return new ParameterizedTypeImpl(TypeToken.of(this).getRawType(), typeArgs, getOwnerType());
}
}
TestPamameterizedImpl.java
package gu.rpc.thrift;
import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
import org.junit.Test;
import com.google.common.reflect.TypeToken;
public class TestPamameterizedImpl {
@SuppressWarnings("serial")
@Test
public void test() {
TypeToken> token = new TypeToken>(){};
System.out.println("oldType:"+token.getType());
ParameterizedType newType = new ParameterizedTypeImpl((ParameterizedType) token.getType()).transform(Long.class, java.util.Date.class);
System.out.println("newType:"+newType);
}
}
测试输出:
oldType:java.util.HashMap.lang.String, java.lang.Long>
newType:java.util.HashMap.lang.String, java.util.Date>