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

[小笔记] 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> 这个复合类型,通过 ArrayListClass 对象拿到的泛型信息就是一个简单的 EE 其实就是一个占位符,而有的 Type 获取到的泛型信息就能够是 String,后面会讲到);如果是返回对象是一个泛型,那么拿到的就是 Object 类的 Class 对象,这就是所谓的泛型擦除;如果没有返回值,那么就是 voidClass 对象。

我就通过我自己写的 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 也是没有的哦,只有一个 ListClass 对象。

  • <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 对象就是 ObjectClass 对象。

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

TypeVariable

描述普通泛型的 TypeTypeVariable 中还有泛型的上界与下界的信息。通过 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 对象能够在反射的时候能够完成更多的有趣的功能。

相关推荐
发仔1234 分钟前
Java的Quartz定时任务引擎详解
java·后端
Seven9715 分钟前
SpringCloud 常见面试题(一)
java
kong790692822 分钟前
SpringCache缓存
java·spring·缓存
程序猿小蒜26 分钟前
基于springboot的汽车资讯网站开发与实现
java·前端·spring boot·后端·spring
それども27 分钟前
SpringBoot 切面AOP获取注解为null
java·spring boot·spring
vx_bisheyuange28 分钟前
基于SpringBoot的热门旅游推荐系统设计与实现
java·spring boot·后端·毕业设计
代码不停29 分钟前
Java分治算法题目练习(快速/归并排序)
java·数据结构·算法
代码or搬砖30 分钟前
SpringBoot整合SpringMVC
java·spring boot·后端
程序定小飞30 分钟前
基于springboot的汽车资讯网站开发与实现
java·开发语言·spring boot·后端·spring
江上清风山间明月36 分钟前
Flutter中Column中使用ListView时溢出问题的解决方法
android·flutter·column·listview