[小笔记] Java 反射中用到的 Type 接口

你现在可能会有疑问,在使用 Java 反射中会用到 Type 接口吗?emmmm,你大概率会用到,Class 目标便是完成了 Type 接口,Type 接口也不是常常使用,通常在反射时可能会用到它,所以也非常容易忘掉,所以这次写一篇文章来简略记录下我遇到过的一些重要的完成 Type 接口的类。

我这儿先给一份我自己测验用的代码:

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 -> %sn", m.toString());
                Type returnType = m.getReturnType();
                System.out.printf("    ReturnType: %s, TypeImplClass: %sn", returnType.getTypeName(), returnType.getClass().getName());
                Type genericReturnType = m.getGenericReturnType();
                System.out.printf("    GenericReturnType: %s, TypeImplClass: %sn", genericReturnType.getTypeName(), genericReturnType.getClass().getName());
                if (genericReturnType instanceof ParameterizedType) {
                    Type[] paramsTypes = ((ParameterizedType) genericReturnType).getActualTypeArguments();
                    for (Type pt: paramsTypes) {
                        System.out.printf("        ParamType: %s, TypeImplClass: %sn", pt.getTypeName(), pt.getClass().getName());
                    }
                }
                if (genericReturnType instanceof TypeVariable) {
                    String typeVariableName = ((TypeVariable<?>) genericReturnType).getName();
                    System.out.printf("        TypeVariableName: %sn", typeVariableName);
                }
                if (genericReturnType instanceof GenericArrayType) {
                    Type genericType = ((GenericArrayType) genericReturnType).getGenericComponentType();
                    System.out.printf("        GenericComponentType: %s, TypeImplClass: %sn", 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> 这个复合类型,经过 ArrayListClass 目标拿到的泛型信息便是一个简略的 EE 其实便是一个占位符,而有的 Type 获取到的泛型信息就可以是 String,后边会讲到);假如是回来目标是一个泛型,那么拿到的便是 Object 类的 Class 目标,这便是所谓的泛型擦除;假如没有回来值,那么便是 voidClass 目标。

我就经过我自己写的 Demo 打印的日志来看看不同的办法回来类型回来的 Class 目标。

  • void test0()
ReturnType: void, TypeImplClass: java.lang.Class
  • String test1()
ReturnType: java.lang.String, TypeImplClass: java.lang.Class
  • List<String> test3()
ReturnType: java.util.List, TypeImplClass: java.lang.Class

这儿咱们看到范型 String 也是没有的哦,只要一个 ListClass 目标。

  • <T> List<T> test4(T t)
ReturnType: java.util.List, TypeImplClass: java.lang.Class

这儿我定了一个泛型 T,回来值是一个复合类型 List<T>,这儿看到和 List<String> 没有区别。

  • <T> T test5(T t)
ReturnType: java.lang.Object, TypeImplClass: java.lang.Class

这儿看到一般的泛型的对应的 Class 目标便是 ObjectClass 目标。

  • String[] test6()
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() 办法来测验看看对应的日志:

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>,其间就有泛型的信息了,然后去拿它的具体泛型的参数,就得到了 StringClass 目标。

TypeVariable

描述一般泛型的 TypeTypeVariable 中还有泛型的上界与下界的信息。经过 TypeVariable#getName() 办法可以拿到泛型的名字。

我以 <T> T test5(T t) 办法来看看测验的日志:

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) 办法来看看测验日志:

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 拿出来的参数类型):

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 目标可以在反射的时分可以完成更多的有趣的功能。