likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 1

[小笔记] 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 -> %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> 这个复合类型,通过 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 对象能够在反射的时候能够完成更多的有趣的功能。