public class TypeTest<T extends TestA, V> {
//TypeVariable
public T param;
//ParameterizedType
public TypeTest<TestA, String> typeTest;
//WildcardType
public TypeTest<? extends TestF,? super V> typeTestWildcardType;
//GenericArrayType :List<String>[] ParameterizedTypeArray V[] typeVariableTypeArray
public void test(List<String>[] ParameterizedTypeArray, V[] typeVariableTypeArray, List<String> list, String[] strings, int[] ints) {
}
}
1.TypeVariable
类型变量, 范型信息在编译时会被转换为一个特定的类型, 而TypeVariable
就是用来反映在JVM编译该泛型前的信息。这个编译该泛型前:也就是说想靠这个是得不到泛型具体是哪个类型的
它的声明是这样的:?
public interface TypeVariable<D extends GenericDeclaration> extends Type
也就是说它跟GenericDeclaration
有一定的联系, 我的理解:
说到TypeVariable类,就不得不提及Java-Type体系中另一个比较重要的接口---GenericDeclaration;含义为:声明类型变量的所有实体的公共接口;也就是说该接口定义了哪些地方可以定义类型变量(泛型);
通过查看源码发现,GenericDeclaration下有三个子类,分别为Class、Method、Constructor;也就是说,我们定义泛型只能在一个类中这3个地方自定义泛型;
此时,我们不禁要问,我们不是经常在类中的属性声明泛型吗,怎么Field没有实现 GenericDeclaration接口呢?
其实,我们在Field中并没有声明泛型,而是在使用泛型而已!
TypeVariable
是指以泛型的类型作为成员变量,例如 类泛型声明Test<T>,声明后在类中使用这个泛型作为成员变量 T param
;
它有如下方法:
Type[] getBounds()
: 获取类型变量的上边界, 若未明确声明上边界则默认为Object
D getGenericDeclaration()
: 获取声明该类型变量实体String getName()
: 获取在源码中定义时的名字
注意:
- 类型变量在定义的时候只能使用
extends
进行(多)边界限定, 不能用super
; - 为什么边界是一个数组? 因为类型变量可以通过
&
进行多个上边界限定,因此上边界有多个
public class TypeMainTest {
public static void main(String[] args) {
//TypeVariable 直接以泛型为类型的属性
try {
Field param = TypeTest.class.getField("param");
if (param.getGenericType() instanceof TypeVariable) {
TypeVariable typeVariable = (TypeVariable) param.getGenericType();
System.out.println("name:" + typeVariable.getName());
System.out.println("GenericDeclaration:" + typeVariable.getGenericDeclaration());
for (Type type : typeVariable.getBounds()) {
System.out.println("Bounds:" + type);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
执行结果:
name:T
GenericDeclaration:class com.example.fragmentadaptertest.type.TypeTest
Bounds:class com.example.fragmentadaptertest.testjava.TestA
2.ParameterizedType
具体的范型类型, 如Map<String, String>
我感觉这个比较好用,例如在反射的时候拿到了一个Map或者List的实例,就可以通过ParameterizedType获取一下里面存的具体是什么类型
有如下方法:
Type getRawType()
: 返回承载该泛型信息的对象, 如上面那个Map<String, String>
承载范型信息的对象是Map
Type[] getActualTypeArguments()
: 返回实际泛型类型列表, 如上面那个Map<String, String>
实际范型列表中有两个元素, 都是String
Type getOwnerType()
: 返回是谁的member.(上面那两个最常用)- getOwnerType通过方法的名称,我们大概了解到,此方法是获取泛型的拥有者,那么拥有者是个什么意思?Returns a {@code?Type} object representing the type that this type? ? * is a member of.? For example, if this type is {@code?O.I},? ? * return a representation of {@code?O}.? (摘自JDK注释)通过注解,我们得知,“拥有者”表示的含义--内部类的“父类”,通过getOwnerType()方法可以获取到内部类的“拥有者”;例如: Map? 就是 Map.Entry<String,String>的拥有者;
public class TypeMainTest {
public static void main(String[] args) {
//ParameterizedType 参数化类型例如:List<String>这种
try {
Field variableTypeTest = TypeTest.class.getField("typeTest");
if (variableTypeTest.getGenericType() instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) variableTypeTest.getGenericType();
System.out.println("RawType:" + parameterizedType.getRawType());
for (Type type : parameterizedType.getActualTypeArguments()) {
System.out.println("type:" + type);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
执行结果:
RawType:class com.example.fragmentadaptertest.type.TypeTest
type:class com.example.fragmentadaptertest.testjava.TestA
type:class java.lang.String
3.GenericArrayType
范型数组,组成数组的元素中有范型则实现了该接口; 它的组成元素是ParameterizedType
或TypeVariable
类型,它只有一个方法:
Type getGenericComponentType()
: 返回数组的组成对象, 即被JVM编译后实际的对象
public class TypeMainTest {
public static void main(String[] args) {
//GenericArrayType 泛型数组,数组的元素的中有泛型
try {
Method method = TypeTest.class.getMethods()[0];
System.out.println(method);
Type[] types = method.getGenericParameterTypes(); //获取泛型参数的type
for (Type type : types) {
if (type instanceof GenericArrayType) {
System.out.println("type:" + type);
System.out.println("GenericArrayType:" + ((GenericArrayType) type).getGenericComponentType());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
执行结果:
public void com.example.fragmentadaptertest.type.TypeTest.test(java.util.List[],java.lang.Object[],java.util.List,java.lang.String[],int[])
type:java.util.List<java.lang.String>[]
GenericArrayType:java.util.List<java.lang.String>
type:V[]
GenericArrayType:V
看结果五个参数只打印了前两个,上面说到了它的组成元素是ParameterizedType
或TypeVariable
类型,上面第三个参数List不是数组,第四第五个是数组没有泛型
4.WildcardType
该接口表示通配符泛型, 比如? extends Number
?和?? super Integer
?它有如下方法:
Type[] getUpperBounds()
: 获取范型变量的上界Type[] getLowerBounds()
: 获取范型变量的下界
注意:
- 现阶段通配符只接受一个上边界或下边界, 返回数组是为了以后的扩展, 实际上现在返回的数组的大小是1
public class TypeMainTest {
public static void main(String[] args) {
//WildcardType
try {
Field field = TypeTest.class.getField("typeTestWildcardType");
ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
Type[] types = parameterizedType.getActualTypeArguments();
for (Type type : types) {
System.out.println("type:" + type);
WildcardType wildcardType = (WildcardType) type;
for (Type mType : wildcardType.getLowerBounds()) {
System.out.println("mTypeLowerBounds:" + mType);
}
for (Type mType : wildcardType.getUpperBounds()) {
System.out.println("mTypeUpperBounds:" + mType);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
执行结果:
type:? extends com.example.fragmentadaptertest.testjava.TestF
mTypeUpperBounds:class com.example.fragmentadaptertest.testjava.TestF
type:? super V
mTypeLowerBounds:V
mTypeUpperBounds:class java.lang.Object
再写几个边界的例子:
List<? extends Number>
, 上界为class java.lang.Number
, 属于Class
类型List<? extends List<T>>
, 上界为java.util.List<T>
, 属于ParameterizedType
类型List<? extends List<String>>
, 上界为java.util.List<java.lang.String>
, 属于ParameterizedType
类型List<? extends T>
, 上界为T
, 属于TypeVariable
类型List<? extends T[]>
, 上界为T[]
, 属于GenericArrayType
类型
下面搞一个gson反序列化的例子来看一下Type的使用:
public class TestD {
public static class Response<T> {
T data;
int code;
String message;
@Override
public String toString() {
return "Response{" + "data=" + data + ", code=" + code + ", message='" + message + '\'' + '}';
}
public Response(T data, int code, String message) {
this.data = data;
this.code = code;
this.message = message;
}
}
public static class Data {
String result;
public Data(String result) {
this.result = result;
}
@Override
public String toString() {
return "Data{" + "result=" + result + '}';
}
}
public static void main(String[] args) {
Response<Data> dataResponse = new Response<>(new Data("数据"), 1, "成功");
Gson gson = new Gson();
String json = gson.toJson(dataResponse);
System.out.println(json);
Type type = new TypeToken<Response<Data>>(){}.getType();
System.out.println(type);
Response<Data> response = gson.fromJson(json,type);
System.out.println(response.data.result);
}
}
执行结果:
{"data":{"result":"数据"},"code":1,"message":"成功"}
com.example.fragmentadaptertest.testjava.TestD$Response<com.example.fragmentadaptertest.testjava.TestD$Data>
数据
如果不用TypeToken:
public static void main(String[] args) {
Response<Data> dataResponse = new Response<>(new Data("数据"), 1, "成功");
Gson gson = new Gson();
String json = gson.toJson(dataResponse);
System.out.println(json);
// Type type = new TypeToken<Response<Data>>(){}.getType();
// System.out.println(type);
Response<Data> response = gson.fromJson(json,Response.class);
System.out.println(response.data.result);
}
发现测试结果报错了:?
因为没有解析泛型的具体类型所以,不知道泛型的具体类型是Data,也就不能这样调用了
{"data":{"result":"数据"},"code":1,"message":"成功"}
Exception in thread "main" java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.example.fragmentadaptertest.testjava.TestD$Data
at com.example.fragmentadaptertest.testjava.TestD.main(TestD.java:49)
在进行GSON反序列化时,存在泛型时,可以借助 TypeToken 获取Type以完成泛型的反序列化。但是为什么 使用TypeToken 要new TypeToken<Response<Data>>(){}创建对应的实现类?
? ? ?因为只有在使用时,创建对应的实现类时,确定泛型类型,编译才能够将泛型 signature信息记录到
Class
元数据中。
看一下TypeToken的源码然后验证一下为什么要
创建对应的实现类:
protected TypeToken() {
this.type = getSuperclassTypeParameter(getClass());
this.rawType = (Class<? super T>) $Gson$Types.getRawType(type);
this.hashCode = type.hashCode();
}
static Type getSuperclassTypeParameter(Class<?> subclass) {
Type superclass = subclass.getGenericSuperclass();
if (superclass instanceof Class) {
throw new RuntimeException("Missing type parameter.");
}
ParameterizedType parameterized = (ParameterizedType) superclass;
return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
}
我们仿照着写个例子:
public class TestC<T> {
public void test1(){
Class<?> aClass = getClass();
Type type = aClass.getGenericSuperclass();
if (type instanceof Class) {
throw new RuntimeException("Missing type parameter.");
}
ParameterizedType parameterized = (ParameterizedType) type;
for (Type type1 : parameterized.getActualTypeArguments()){
System.out.println(type1);
}
}
}
public class TypeMainTest {
public static void main(String[] args) {
TestC<TestD.Response<TestD.Data>> testC = new TestC<>();
testC.test1();
}
}
测试结果:
发现不创建对应的实现类,aClass.getGenericSuperclass()还是Class类型,并没有解析成泛型对应的Type类型
Exception in thread "main" java.lang.RuntimeException: Missing type parameter.
at com.example.fragmentadaptertest.testjava.TestC.test1(TestC.java:14)
at com.example.fragmentadaptertest.type.TypeMainTest.main(TypeMainTest.java:25)
改成创建对应的实现类:
创建实现类的时候,必须在后面也加上这个泛型的类型,否则编译不通过,估计就是因为上面说的:?因为只有在使用时,创建对应的实现类时,确定泛型类型,编译才能够将泛型 signature信息记录到Class元数据中。
public class TypeMainTest {
public static void main(String[] args) {
TestC<TestD.Response<TestD.Data>> testC = new TestC<TestD.Response<TestD.Data>>(){};
testC.test1();
}
}
测试结果:
com.example.fragmentadaptertest.testjava.TestD$Response<com.example.fragmentadaptertest.testjava.TestD$Data>
得到了泛型的真实类型,可见TypeToken<T>就是一个工具类,它的作用就是用来解析它泛型T的类型的,我们自己也可以实现。
Type及其子接口的来历
泛型出现之前的类型
没有泛型的时候,只有原始类型。此时,所有的原始类型都通过字节码文件类Class类进行抽象。Class类的一个具体对象就代表一个指定的原始类型。
泛型出现之后的类型
泛型出现之后,扩充了数据类型。从只有原始类型扩充了参数化类型、类型变量类型、限定符类型 、泛型数组类型。
与泛型有关的类型不能和原始类型统一到Class的原因
原始类型和新产生的类型都应该统一成各自的字节码文件类型对象。但是由于泛型不是最初Java中的成分。如果真的加入了泛型,涉及到JVM指令集的修改,这是非常致命的。
为了使用泛型又不真正引入泛型,Java采用泛型擦除机制来引入泛型。Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的麻烦。但是,一旦编译完成,所有的和泛型有关的类型全部擦除。
因此,与泛型有关的参数化类型、类型变量类型、限定符类型 、泛型数组类型这些类型编译后全部被打回原形,在字节码文件中全部都是泛型被擦除后的原始类型,并不存在和自身类型对应的字节码文件。所以和泛型相关的新扩充进来的类型不能被统一到Class类中。
为了通过反射操作这些类型以迎合实际开发的需要,Java就新增了ParameterizedType, TypeVariable<D>, GenericArrayType, WildcardType
几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。
为了程序的扩展性,最终引入了Type接口作为Class和ParameterizedType, TypeVariable<D>, GenericArrayType, WildcardType
这几种类型的总的父接口。这样可以用Type类型的参数来接受以上五种子类的实参或者返回值类型就是Type类型的参数。统一了与泛型有关的类型和原始类型Class
从上面看到,Type的出现仅仅起到了通过多态来达到程序扩展性提高的作用,没有其他的作用。因此Type接口的源码中没有任何方法。