[小笔记] 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 对象能够在反射的时候能够完成更多的有趣的功能。

相关推荐
stevenzqzq1 分钟前
Android Koin 注入入门教程
android·kotlin
xunyan62342 分钟前
第九章 JAVA常用类
java·开发语言
China_Yanhy18 分钟前
AWS S3 深度配置指南:每一栏每个选项有什么作用
java·数据库·aws
秃了也弱了。27 分钟前
FASTJSON库:阿里出品java界json解析库,使用与踩坑记录
java·开发语言·json
炼金术30 分钟前
SkyPlayer v1.1.0 - 在线视频播放功能更新
android·ffmpeg
用户2760381578132 分钟前
鲲鹏+昇腾:开启 AI for Science 新范式——基于PINN的流体仿真加速实践
android
安全渗透Hacker40 分钟前
参数未校验导致的DOS(服务拒绝)问题典型场景
java·安全·web安全·网络安全·安全性测试
Chan161 小时前
微服务 - Higress网关
java·spring boot·微服务·云原生·面试·架构·intellij-idea
二哈喇子!1 小时前
JavaSE 与 JavaEE 知识点整合
java·servlet·tomcat
此去正年少1 小时前
编写adb脚本工具对Android设备上的闪退问题进行监控分析
android·adb·logcat·ndk·日志监控