[小笔记] Java 反射中用到的 Type 接口
你现在可能会有疑问,在使用 Java
反射中会用到 Type
接口吗?emmmm,你大概率会用到,Class
对象就是实现了 Type
接口,Type
接口也不是经常使用,通常在反射时可能会用到它,所以也非常容易忘记,所以这次写一篇文章来简单记录下我遇到过的一些重要的实现 Type
接口的类。
我这里先给一份我自己测试用的代码:
Java
public class Main {
void test0() {
}
String test1() {
return "Hello, World.";
}
List<?> test2() {
ArrayList<String> l = new ArrayList<String>();
l.add("Hello, World.");
return l;
}
List<String> test3() {
ArrayList<String> l = new ArrayList<String>();
l.add("Hello, World.");
return l;
}
<T> List<T> test4(T t) {
ArrayList<T> l = new ArrayList<>();
l.add(t);
return l;
}
<T> T test5(T t) {
return t;
}
String[] test6() {
String[] strings = new String[1];
strings[0] = "Hello, World.";
return strings;
}
<T> T[] test7(T t, Class<T> clazz) {
ArrayList<T> l = new ArrayList<>();
l.add(t);
return (T[]) l.toArray();
}
void printTestReturnType() {
Class<?> clazz = Main.class;
Method[] methods = clazz.getDeclaredMethods();
for (Method m: methods) {
String methodName = m.getName();
if (methodName.startsWith("test")) {
System.out.printf("Method -> %s\n", m.toString());
Type returnType = m.getReturnType();
System.out.printf(" ReturnType: %s, TypeImplClass: %s\n", returnType.getTypeName(), returnType.getClass().getName());
Type genericReturnType = m.getGenericReturnType();
System.out.printf(" GenericReturnType: %s, TypeImplClass: %s\n", genericReturnType.getTypeName(), genericReturnType.getClass().getName());
if (genericReturnType instanceof ParameterizedType) {
Type[] paramsTypes = ((ParameterizedType) genericReturnType).getActualTypeArguments();
for (Type pt: paramsTypes) {
System.out.printf(" ParamType: %s, TypeImplClass: %s\n", pt.getTypeName(), pt.getClass().getName());
}
}
if (genericReturnType instanceof TypeVariable) {
String typeVariableName = ((TypeVariable<?>) genericReturnType).getName();
System.out.printf(" TypeVariableName: %s\n", typeVariableName);
}
if (genericReturnType instanceof GenericArrayType) {
Type genericType = ((GenericArrayType) genericReturnType).getGenericComponentType();
System.out.printf(" GenericComponentType: %s, TypeImplClass: %s\n", genericType.getTypeName(), genericType.getClass().getName());
}
System.out.println();
}
}
}
public static void main(String[] args) {
Main m = new Main();
m.printTestReturnType();
}
}
我使用了上面的 Main
对象中的多个 test
开头的方法的返回值来测试。
Class
Class
对象是用来描述所有 Java
中的类信息,在类第一次使用时就会被加载到方法区中,后续再加载就用之前已经加载好的 Class
信息。
回到我自己写的 Demo
中,通过 Method#getReturnType()
方法就能够获取到对应返回对象的 Class
对象。如果返回对象是带泛型的对象,它的详细的泛型 Type
信息也会被抹除掉,这么说也有点怪怪的,因为 Class
对象中本来就没有详细的泛型 Type
信息(比如说 ArrayList<String>
这个复合类型,通过 ArrayList
的 Class
对象拿到的泛型信息就是一个简单的 E
,E
其实就是一个占位符,而有的 Type
获取到的泛型信息就能够是 String
,后面会讲到);如果是返回对象是一个泛型,那么拿到的就是 Object
类的 Class
对象,这就是所谓的泛型擦除;如果没有返回值,那么就是 void
的 Class
对象。
我就通过我自己写的 Demo
打印的日志来看看不同的方法返回类型返回的 Class
对象。
void test0()
text
ReturnType: void, TypeImplClass: java.lang.Class
String test1()
text
ReturnType: java.lang.String, TypeImplClass: java.lang.Class
List<String> test3()
text
ReturnType: java.util.List, TypeImplClass: java.lang.Class
这里我们看到范型 String
也是没有的哦,只有一个 List
的 Class
对象。
<T> List<T> test4(T t)
text
ReturnType: java.util.List, TypeImplClass: java.lang.Class
这里我定了一个泛型 T
,返回值是一个复合类型 List<T>
,这里看到和 List<String>
没有区别。
<T> T test5(T t)
text
ReturnType: java.lang.Object, TypeImplClass: java.lang.Class
这里看到普通的泛型的对应的 Class
对象就是 Object
的 Class
对象。
String[] test6()
text
ReturnType: java.lang.String[], TypeImplClass: java.lang.Class
上面就是普通的对象数组对应的 Class
对象。
ParameterizedType
通过 Method#getReturnType()
得到的是一个 Class
对象,其中没有运行时详细的泛型信息。通过 Method#getGenericReturnType()
方法就能够获取到包含详细泛型信息的 Type
,这里就不是固定的 Class
对象了,其中 ParameterizedType
对象就是一个非常常见的复合类型的 Type
,通过它的 ParameterizedType#getActualTypeArguments()
方法就能够获取到其泛型的 Type
。
我以 List<String> test3()
方法来测试看看对应的日志:
text
Method -> java.util.List com.tans.test.Main.test3()
ReturnType: java.util.List, TypeImplClass: java.lang.Class
GenericReturnType: java.util.List<java.lang.String>, TypeImplClass: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
ParamType: java.lang.String, TypeImplClass: java.lang.Class
我们看到返回的 Type
的实现类是 ParameterizedTypeImpl
,它实现了 ParameterizedType
接口,我们看到它的名字是 java.util.List<java.lang.String>
,其中就有泛型的信息了,然后去拿它的具体泛型的参数,就得到了 String
的 Class
对象。
TypeVariable
描述普通泛型的 Type
,TypeVariable
中还有泛型的上界与下界的信息。通过 TypeVariable#getName()
方法能够拿到泛型的名字。
我以 <T> T test5(T t)
方法来看看测试的日志:
text
Method -> java.lang.Object com.tans.test.Main.test5(java.lang.Object)
ReturnType: java.lang.Object, TypeImplClass: java.lang.Class
GenericReturnType: T, TypeImplClass: sun.reflect.generics.reflectiveObjects.TypeVariableImpl
TypeVariableName: T
GenericArrayType
泛型数组对应的类型,通过 GenericArrayType#getGenericComponentType()
方法能够拿到对应的泛型 Type
,这方面和 ParameterizedType
有点类似,只不过 ParameterizedType
返回的是一个 Type
的数组,而 GenericArrayType
返回的是一个固定的 Type
,这也很好理解,数组只能有一个泛型参数。
我以 <T> T[] test7(T t, Class<T> clazz)
方法来看看测试日志:
text
Method -> java.lang.Object[] com.tans.test.Main.test7(java.lang.Object,java.lang.Class)
ReturnType: java.lang.Object[], TypeImplClass: java.lang.Class
GenericReturnType: T[], TypeImplClass: sun.reflect.generics.reflectiveObjects.GenericArrayTypeImpl
GenericComponentType: T, TypeImplClass: sun.reflect.generics.reflectiveObjects.TypeVariableImpl
WildcardType
如果有一个复合类型 List<?>
,其中那个 ?
对应的类型就是 WildcardType
,所以通过 Method#getGenericReturnType()
永远不会返回这种类型。emm,怎么翻译这种类型呢?就是不知道什么类型的泛型吧。
我以 List<?> test2()
方法来看看测试日志(注意是从 ParameterizedType
拿出来的参数类型):
text
Method -> java.util.List com.tans.test.Main.test2()
ReturnType: java.util.List, TypeImplClass: java.lang.Class
GenericReturnType: java.util.List<?>, TypeImplClass: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
ParamType: ?, TypeImplClass: sun.reflect.generics.reflectiveObjects.WildcardTypeImpl
最后
希望读了本篇文章后对反射中的 Type
对象会有多一点点的认识,通过 Type
对象能够在反射的时候能够完成更多的有趣的功能。