对于 Base<F,T>
这样的带泛型参数的抽象类,如何在类初始化时得到F,T对应的类型?
Guava TypeToken是一个功能强大的泛型解析工具。可以很简单的实际这个需求。
如下一行代码就可以完成解析:
java
Type resolved = new TypeToken<F>(getClass()) {}.getType();
在执行 TypeToken(Class<?> declaringClass)
构造方法时已经完成了对F类型参数的解析,保存到runtimeType
字段。如下为 TypeToken(Class<?> declaringClass)
构造方法的代码:
java
/**
* 这里declaringClass必须为声明类型变量的类
*/
protected TypeToken(Class<?> declaringClass) {
Type captured = super.capture();
if (captured instanceof Class) {
this.runtimeType = captured;
} else {
/**
* 非Class的泛型变量,则调用 resolveType 方法解析 T 为具体参数
* 由declaringClass创建的TypeToken对象完整保存了类型变量到实际类型的对应表,
* 所以resolveType方法就是通过查表得到对应具体类型
*/
this.runtimeType = of(declaringClass).resolveType(captured).runtimeType;
}
}
调用示例
java
import static org.junit.Assert.*;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import com.google.common.reflect.TypeToken;
/**
* Guava TypeToken 解析类型变量(TypeVariable)测试
* @author l0km
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class ResolveTypeTest {
@SuppressWarnings("serial")
@Test
public void test() {
Base<String,Date> base = new Base<String,Date>() {};
assertEquals(String.class, base.inputType);
assertEquals(Date.class, base.outputType);
Sub1<Date> sub1 = new Sub1<Date>() {};
assertEquals(String.class, sub1.inputType);
assertEquals(Date.class, sub1.outputType);
Sub2<Map<Integer,Type>> sub2 = new Sub2<Map<Integer,Type>>() {};
assertEquals(new TypeToken<Map<Integer,Type>>() {}.getType(), sub2.inputType);
assertEquals(String.class, sub2.outputType);
Sub3<List<Date>> sub3 = new Sub3<List<Date>>() {};
assertEquals(String.class, sub3.inputType);
assertEquals(new TypeToken<List<Date>>() {}.getType(), sub3.outputType);
Sub4<int[]> sub4 = new Sub4<int[]>() {};
assertEquals(int[].class, sub4.inputType);
assertEquals(String.class, sub4.outputType);
}
public static abstract class Base<F,T>{
final Type inputType;
final Type outputType;
@SuppressWarnings("serial")
protected Base(){
inputType = new TypeToken<F>(getClass()) {}.getType();
System.out.printf("inputType: %s\n",inputType);
outputType = new TypeToken<T>(getClass()) {}.getType();
System.out.printf("outputType: %s\n",outputType);
}
}
public static abstract class Sub1<T> extends Base<String,T>{
}
public static abstract class Sub2<F> extends Base<F,String>{
}
public static abstract class Sub3<T> extends Sub1<T>{
}
public static abstract class Sub4<F> extends Sub2<F>{
}
}