java Type 详解

2023-11-10

转自:https://blog.csdn.net/gdutxiaoxu/article/details/68926515

为什么要写这一系列的博客呢?

因为在 Android 开发的过程中, 泛型,反射,注解这些知识进场会用到,几乎所有的框架至少都会用到上面的一两种知识,如 Gson 就用到泛型,反射,注解,Retrofit 也用到泛型,反射,注解 。学好这些知识对我们进阶非常重要,尤其是阅读开源框架源码或者自己开发开源框架。

java Type 详解

java 反射机制详解

注解使用入门(一)

Android 自定义编译时注解1 - 简单的例子

Android 编译时注解 —— 语法详解

带你读懂 ButterKnife 的源码
前言

错误可分为两种:编译时错误与运行时错误。编译时错误在编译时可以发现并排除,而运行时错误具有很大的不确定性,在程序运行时才能发现,造成的后果可能是灾难性的。

泛型的引入使得一部分错误可以提前到编译时期发现,极大地增强了代码的健壮性。但是我们知道 java 泛型在运行的时候是会进行泛型擦除的,那我们要怎样得到在编译时期泛型的信息呢?java 为我们提供了 Type 接口,使用它,我们可以得到这些信息。

不知道什么是泛型擦除的同学可以看一下

类型擦除是指泛型在运行的时候会去除泛型的类型信息。java 中,泛型主要是在编译层次来实现的,在生成的字节码即 class 文件是不包括泛型的 类型信息的。 即 List, List ,List 虽然在编译时候是不同的,但是在编译完成后,在class 文件 中都只会把他们当作 List 来对待。
Type 接口简介
类 UML 图如下

简单来说:Type是所有类型的父接口, 如原始类型(raw types 对应 Class)、 参数化类型(parameterized types 对应 ParameterizedType)、 数组类型(array types 对应 GenericArrayType)、 类型变量(type variables 对应 TypeVariable )和基本(原生)类型(primitive types 对应 Class),。

子接口有 ParameterizedType, TypeVariable, GenericArrayType, WildcardType, 实现类有Class。
ParameterizedType (参数化类型)

官方文档的说明是这样的

    ParameterizedType represents a parameterized type such as
    Collection<String>

需要注意的是,并不只是 Collection<String> 才是 parameterized,任何类似于 ClassName 这样的类型都是 ParameterizedType ,比如下面的这些都是 parameterizedType.

    Map<String, Person> map;
    Set<String> set1;
    Class<?> clz;
    Holder<String> holder;
    List<String> list;

    static class Holder<V>{

    }

而类似于这样的 ClassName 不是 ParameterizedType.

Set set;
List aList;

ParameterizedType 的几个主要方法

    Type[] getActualTypeArguments();
    Type getRawType();
    Type getOwnerType();

Type[] getActualTypeArguments(); 返回 这个 Type 类型的参数的实际类型数组。 如 Map<String,Person> map 这个 ParameterizedType 返回的是 String 类,Person 类的全限定类名的 Type Array。

Type getRawType() 返回的是当前这个 ParameterizedType 的类型。 如 Map<String,Person> map 这个 ParameterizedType 返回的是 Map 类的全限定类名的 Type Array。

Type getOwnerType();

    Returns a {@code Type} object representing the type that this type is a member of.

这个比较少用到。返回的是这个 ParameterizedType 所在的类的 Type (注意当前的 ParameterizedType 必须属于所在类的 member)。解释起来有点别扭,还是直接用代码说明吧。 比如 Map<String,Person> map 这个 ParameterizedType 的 getOwnerType() 为 null,而 Map.Entry<String, String>entry 的 getOwnerType() 为 Map 所属于的 Type。

说了这么多,下面我们一起来看一下例子,加深印象。

public class ParameterizedTypeBean {
    // 下面的 field 的 Type 属于 ParameterizedType
    Map<String, Person> map;
    Set<String> set1;
    Class<?> clz;
    Holder<String> holder;
    List<String> list;
    // Map<String,Person> map 这个 ParameterizedType 的 getOwnerType() 为 null,
    // 而 Map.Entry<String, String> entry 的 getOwnerType() 为 Map 所属于的 Type。
    Map.Entry<String, String> entry;
    // 下面的 field 的 Type 不属于ParameterizedType
    String str;
    Integer i;
    Set set;
    List aList;

    static class Holder<V> {

    }
}

public class TestHelper {

    public static void testParameterizedType() {
        Field f = null;
        try {
            Field[] fields = ParameterizedTypeBean.class.getDeclaredFields();
            // 打印出所有的 Field 的 TYpe 是否属于 ParameterizedType
            for (int i = 0; i < fields.length; i++) {
                f = fields[i];
                PrintUtils.print(f.getName()
                        + " getGenericType() instanceof ParameterizedType "
                        + (f.getGenericType() instanceof ParameterizedType));
            }
            getParameterizedTypeMes("map" );
            getParameterizedTypeMes("entry" );


        } catch (NoSuchFieldException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    private static void getParameterizedTypeMes(String fieldName) throws NoSuchFieldException {
        Field f;
        f = ParameterizedTypeBean.class.getDeclaredField(fieldName);
        f.setAccessible(true);
        PrintUtils.print(f.getGenericType());
        boolean b=f.getGenericType() instanceof ParameterizedType;
        PrintUtils.print(b);
        if(b){
            ParameterizedType pType = (ParameterizedType) f.getGenericType();
            PrintUtils.print(pType.getRawType());
            for (Type type : pType.getActualTypeArguments()) {
                PrintUtils.print(type);
            }
            PrintUtils.print(pType.getOwnerType()); // null
        }
    }
}
    print:map getGenericType() instanceof ParameterizedType true

    print:set1 getGenericType() instanceof ParameterizedType true

    print:clz getGenericType() instanceof ParameterizedType true

    print:holder getGenericType() instanceof ParameterizedType true

    print:list getGenericType() instanceof ParameterizedType true

    print:str getGenericType() instanceof ParameterizedType false

    print:i getGenericType() instanceof ParameterizedType false

    print:set getGenericType() instanceof ParameterizedType false

    print:aList getGenericType() instanceof ParameterizedType false

    print:entry getGenericType() instanceof ParameterizedType true

    print:java.util.Map

TypeVariable 变量

比如 public class TypeVariableBean
主要方法

    Type[] getBounds(); 得到上边界的 Type数组,如 K 的上边界数组是 InputStream 和 Serializable。 V 没有指定的话,上边界是 Object
    D getGenericDeclaration(); 返回的是声明这个 Type 所在的类 的 Type
    String getName(); 返回的是这个 type variable 的名称

public class TypeVariableBean<K extends InputStream & Closeable, V> {
    // K 的上边界是 InputStream
    K key;
    // 没有指定的话 ,V 的 上边界 属于 Object
    V value;
    // 不属于 TypeTypeVariable
    V[] values;
    String str;
    List<K> kList;

}

TypeVariableBean bean = new TypeVariableBean<FileInputStream, String>();
fk = TypeVariableBean.class.getDeclaredField("key");
eyType = (TypeVariable) fk.getGenericType();
System.out.println(keyType.getName());System.out.println(keyType.getGenericDeclaration());

执行上述代码,将可以看到如下的效果

    K

    class com.xujun.gennericity.beans.TypeVariableBean

GenericArrayType

    represents an array type whose component
    type is either a parameterized type or a type variable.

简单来说就是:范型数组,组成数组的元素中有范型则实现了该接口; 它的组成元素是 ParameterizedType 或 TypeVariable 类型

// 属于 GenericArrayType
List<String>[] pTypeArray;
// 属于 GenericArrayType
T[] vTypeArray;
// 不属于 GenericArrayType
List<String> list;
// 不属于 GenericArrayType
String[] strings;
// 不属于 GenericArrayType
Person[] ints;

下面我们一起来看一下例子

public class GenericArrayTypeBean<T> {

    public void test(List<String>[] pTypeArray, T[] vTypeArray,
             List<String> list, String[] strings, Person[] ints) {
        }
}

    public static void testGenericArrayType() {
        Method method = GenericArrayTypeBean.class.getDeclaredMethods()[0];
        System.out.println(method);
        // public void test(List<String>[] pTypeArray, T[]
        // vTypeArray,List<String> list, String[] strings, Person[] ints)
        Type[] types = method.getGenericParameterTypes(); // 这是 Method 中的方法
        for (Type type : types) {
            System.out.println(type instanceof GenericArrayType);// 依次输出true,true,false,false,false
        }
    }

输出结果

    public void com.xujun.gennericity.beans.GenericArrayTypeBean.test(java.util.List[],java.lang.Object[],java.util.List,java.lang.String[],com.xujun.gennericity.Person[])

    true

    true

    false

    false

    false

WildcardType 通配符的类型

    {@code ?}, {@code ? extends Number}, or {@code ? super Integer} 这些类型 都属于 WildcardType

extends 用来指定上边界,没有指定的话上边界默认是 Object, super 用来指定下边界,没有指定的话为 null。

几个主要方法介绍

    Type[] getLowerBounds() 得到上边界 Type 的数组
    Type[] getUpperBounds() 得到下边界 Type 的数组

下面一起来看一下例子。

public class WildcardTypeBean {
    private List<? extends Number> a;  // a没有下界,
//  没有指定的话,上边界默认是 Object ,下边界是  String                      
    private List<? super String> b;

    private List<String> c;

    private Class<?> aClass;
}


    public static void testWildCardType() {
        try {
            Field[] fields = WildcardTypeBean.class.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                Field field = fields[i];
                Type type = field.getGenericType();
                String nameString = field.getName();
                PrintUtils.print("下面开始打印" + nameString + "是否具有通配符");
                if (!(type instanceof ParameterizedType)) {
                    PrintUtils.print("---------------------------");
                    continue;
                }
                ParameterizedType parameterizedType = (ParameterizedType) type;
                type = parameterizedType.getActualTypeArguments()[0];
                if (!(type instanceof WildcardType)) {
                    PrintUtils.print("---------------------------");
                    continue;
                }
                WildcardType wildcardType = (WildcardType) type;
                Type[] lowerTypes = wildcardType.getLowerBounds();
                if (lowerTypes != null) {
                    PrintUtils.print("下边界");
                    PrintUtils.printTypeArr(lowerTypes);
                }
                Type[] upTypes = wildcardType.getUpperBounds();
                if (upTypes != null) {
                    PrintUtils.print("上边界");
                    PrintUtils.printTypeArr(upTypes);
                }
                PrintUtils.print("---------------------------");

            }
            Field fieldA = WildcardTypeBean.class.getDeclaredField("a");
            Field fieldB = WildcardTypeBean.class.getDeclaredField("b");
            // 先拿到范型类型
            PrintUtils.print(fieldA.getGenericType() instanceof ParameterizedType);
            PrintUtils.print(fieldB.getGenericType() instanceof ParameterizedType);
            ParameterizedType pTypeA = (ParameterizedType) fieldA.getGenericType();
            ParameterizedType pTypeB = (ParameterizedType) fieldB.getGenericType();
            // 再从范型里拿到通配符类型
            PrintUtils.print(pTypeA.getActualTypeArguments()[0] instanceof WildcardType);
            PrintUtils.print(pTypeB.getActualTypeArguments()[0] instanceof WildcardType);
            WildcardType wTypeA = (WildcardType) pTypeA.getActualTypeArguments()[0];
            WildcardType wTypeB = (WildcardType) pTypeB.getActualTypeArguments()[0];
            // 方法测试
            System.out.println(wTypeA.getUpperBounds()[0]);
            System.out.println(wTypeB.getLowerBounds()[0]);
            // 看看通配符类型到底是什么, 打印结果为: ? extends java.lang.Number
            System.out.println(wTypeA);
        } catch (NoSuchFieldException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

java Type 总结

Type及其子接口的来历

    泛型出现之前的类型

没有泛型的时候,只有原始类型。此时,所有的原始类型都通过字节码文件类Class类进行抽象。Class类的一个具体对象就代表一个指定的原始类型。

    泛型出现之后的类型

泛型出现之后,扩充了数据类型。从只有原始类型扩充了参数化类型、类型变量类型、限定符类型 、泛型数组类型。
为什么会产生泛型擦除的原因

我们知道在 jdk 1.5 以前的时候,是没有 泛型的。在 jdk 1.5 的时候,才引入了泛型。如果真的在动态运行的时候加入泛型,涉及到 JVM 命令的修改,这无疑是非常致命的。因此折中采取了这样的策略,在编译的时候进行检查,在运行的时候进行擦除,也是我们说的泛型擦除。 同时这也说明一点,在设计框架的时候,框架的健壮性和灵活性非常重要。
为什么要学习 Type

我们知道现在的框架都会使用泛型,掌握 Type 有利于我们读懂它们的源码,或者自己动手打造框架。如 Android 的常用开源框架 Gson ,Retrofit等。
题外话

最近更新博客的频率有点低,主要是因为惰性吧。每天实习完以后,有点累,就不太想写博客了。我现在也不知道我能坚持到什么时候,顺其自然吧。PS,真的越来越佩服那些坚持写博客的人,你们是最棒的。

文章首发地址 CSDN

Demo下载地址
---------------------
作者:gdutxiaoxu
来源:CSDN
原文:https://blog.csdn.net/gdutxiaoxu/article/details/68926515?utm_source=copy
版权声明:本文为博主原创文章,转载请附上博文链接!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java Type 详解 的相关文章

  • Jackson JSON + Java 泛型

    我正在尝试将以下 JSON 反序列化 映射到List
  • Android:如何暂停和恢复可运行线程?

    我正在使用 postDelayed 可运行线程 当我按下按钮时 我需要暂停并恢复该线程 请任何人帮助我 这是我的主题 protected void animation music6 music4 postDelayed new Runnab
  • “源兼容性”和“目标兼容性”有什么区别?

    之间有什么关系 区别sourceCompatibility and targetCompatibility 当它们设置为不同的值时会发生什么 根据工具链和兼容性 https docs gradle org current userguide
  • 连接外部 Accumulo 实例和 java

    我正在尝试使用 Accumulo 连接到虚拟机 问题是 我无法将其连接到 Java 中 我可以看到 Apache 抛出的网页 但我无法让它与代码一起工作 我认为这是缺乏知识的问题而不是真正的问题 但我找不到这方面的文档 所有示例都使用 lo
  • Kafka - 如何同时使用过滤器和过滤器?

    我有一个 Kafka 流 它从一个主题获取数据 并且需要将该信息过滤到两个不同的主题 KStream
  • 以相反的顺序打印任何集合中的项目?

    我在 使用 Java 进行数据结构和问题解决 一书中遇到以下问题 编写一个例程 使用 Collections API 以相反的顺序打印任何 Collection 中的项目 不要使用 ListIterator 我不会把它放在这里 因为我想让有
  • 如何在 Angular 2 应用程序中从 TypeScript/JavaScript 中的字符串获取类?

    在我的应用程序中 我有这样的内容 user ts export class User 现在 我这样做 应用程序组件 ts callAnotherFunction User 如果我将类名作为字符串 即 我该如何做到这一点 User 如果可能的
  • 为什么 jar 执行的通配符在 docker CMD 中不起作用?

    我有一个Dockerfile与以下CMD启动我的 Spring Boot 应用程序 FROM java 8 jre CMD java jar app file jar 当我尝试从创建的图像启动容器时 我得到 Error Unable to
  • 为什么在将 String 与 null 进行比较时会出现 NullPointerException?

    我的代码在以下行中出现空指针异常 if stringVariable equals null 在此语句之前 我声明了 stringVariable 并将其设置为数据库字段 在这个声明中 我试图检测该字段是否有null值 但不幸的是它坏了 有
  • 如何使用双重调度来分析图形基元的交集?

    我正在分析图形基元 矩形 直线 圆形等 的交互并计算重叠 相对方向 合并等 这被引用为双重调度的一个主要示例 例如维基百科 http en wikipedia org wiki Double dispatch 自适应碰撞算法通常要求 不同的
  • 使用 Proguard 通过 Dropbox.com 库混淆 Android 应用程序

    我刚刚创建了一个需要 Dropbox com API 库的 Android 应用程序 我现在尝试在 发布 模式下构建应用程序 并希望在代码上运行混淆器以对其进行混淆 但是 每当我尝试运行 Proguard 时 都会收到以下错误 Progua
  • Android - 存储对ApplicationContext的引用

    我有一个静态 Preferences 类 其中包含一些应用程序首选项和类似的内容 可以在那里存储对 ApplicationContext 的引用吗 我需要该引用 以便我可以在不继承 Activity 的类中获取缓存文件夹和类似内容 你使用的
  • 从三点求圆心的算法是什么?

    我在圆的圆周上有三个点 pt A A x A y pt B B x B y pt C C x C y 如何计算圆心 在Processing Java 中实现它 我找到了答案并实施了一个可行的解决方案 pt circleCenter pt A
  • 如何向页面添加 HTML 页眉和页脚?

    如何使用 itext 从 html 源添加标题到 pdf 目前 我们已经扩展了 PdfPageEventHelper 并重写了这些方法 工作正常 但当我到达 2 个以上页面时 它会抛出 RuntimeWorkerException Over
  • Tomcat 6 未从 WEB-INF/lib 加载 jar

    我正在尝试找出我的 tomcat 环境中的配置问题 我们的生产服务器正在运行 tomcat 安装并从共享 NFS 挂载读取战争 然而 当我尝试使用独立的盒子 及其配置 进行同样的战争时 我收到下面发布的错误 有趣的是 如果我将 WEB IN
  • Lombok 不适用于 Eclipse Neon

    我下载了lombok jar lombok 1 16 14 jar 并将其放入我的下载中 然后我点击这个 jar 执行正确地识别了我的 MacOS 上的 Eclipse 实例 然后我选择了我想要的实例 Lombok也在pom xml中指定
  • Azure Java SDK:ServiceException:ForbiddenError:

    尝试了基本位置检索器代码 如下所示 String uri https management core windows net String subscriptionId XXXXXXXX 5fad XXXXXX 9dfa XXXXXX St
  • 确定 JavaFX 中是否消耗了事件

    我正在尝试使用 JavaFX 中的事件处理来做一些非滑雪道的事情 我需要能够确定手动触发事件后是否已消耗该事件 在以下示例中 正确接收了合成鼠标事件 但调用 Consumer 不会更新该事件 我对此进行了调试 发现 JavaFX 实际上创建
  • Selenium 单击在 Internet Explorer 11 上不起作用

    我尝试在 Internet Explorer 上单击 selenium 但它不起作用 我努力了element click moveToElement element click build perform javascript没事了 事实上
  • 在会话即将到期之前调用方法

    我的网络应用程序有登录的用户 有一个超时 在会话过期之前 我想执行一个方法来清理一些锁 我已经实现了sessionListener但一旦我到达public void sessionDestroyed HttpSessionEvent eve

随机推荐

  • windows上bug崩溃定位分析(Qt或者VS)

    任何情况下 都不能保证自己写的代码不会发生崩溃 崩溃不可怕 可怕的是无法定位哪里崩溃 特别是客户那边崩溃 开发者这边不崩溃 问题陷入僵局 自从有了下面这个神奇的代码 再也不怕了 以下代码亲自测试没问题 1 如果是在VC 中 那么只需要将下列
  • 干货系列三:一台服务器能承载多少人同时访问?

    有很多人都会问这个问题 服务器能承载多少人同时访问 这个问题其实是很难有一个非常准确的答案的 因为服务器能同时承载的在线人数是受到多方面因素共同影响的结果 比如带宽 服务器处理速度以及访问页面的大小等等因素 虽然很难有一个精确的答案 但是服
  • 60秒轻松计算出任意一年任意一天星期几?

    60秒轻松计算出任意一年任意一天星期几 一 提出问题 60秒就可以轻松计算出任意一年任意一天星期几吗 你相信吗 如果能算出 连脑神经专家都认为是神童 大家可以通过度娘搜索 张戈 自闭症 连人民网都有报道 有图为证 如何快速计算出任意一年任意
  • Spring Security详解

    第一节 Spring Security 简介 Spring 是一个非常流行和成功的 Java 应用开发框架 Spring Security 基于 Spring 框架 提供了一套 Web 应用安全性的完整解决方案 一般来说 Web 应用的安全
  • maven高级学习

    maven高级 一 idea创建maven项目 1 1 idea中创建maven web项目 1 2 idea中使用tomcat 1 3 插件和依赖的区别 二 依赖管理 2 1 依赖配置 2 2 依赖传递 2 2 1 依赖传递中的冲突问题
  • Yii Framework 开发教程(14) UI 组件 MaskedTextField示例

    CMaskedTextField为格式输入框 可以为文本框指定Mask限制用户可以出入的文本格式 如本例使用99 99 9999 可以只允许输入类似日期的文本 修改View 添加CMaskedTextField 组件 php view pl
  • java比较mysql两个数据库_用java实现操作两个数据库的数据

    1 首先需要在jdbc的配置文件里面配置两个数据库的连接 数据库1的配置 driverClassName com mysql jdbc Driver url jdbc mysql 地址 3306 数据库名 useUnicode true c
  • 【SQL入门系列二】SQLZOO 分组

    分组 5 SUM and COUNT 5 SUM and COUNT Aggregate functions SUM COUNT MAX AVG SUM 世界总人口 SELECT SUM population FROM world Afri
  • springboot整合ehcache

    springboot整合ehcache springboot版本 2 5 1 1 pom文件
  • 12、计算机图形学——几何(网格细分与网格简化)

    一 网格细分 1 1 概念 网格细分指的是将原有模型上的网格分成更多个网格 从而将模型变得更加精细 提高渲染出来的效果 让画面更加漂亮 下图就是一个网格细分的示意 左图是细分前的效果 右图是细分后的效果 可见 细分后的面数更多 模型更加精细
  • java将网络图片下载并压缩导出到本地

    java将网络图片下载并压缩导出到本地 package com example demo ChartGraphics test import org apache tools zip ZipEntry import org apache t
  • 使用VsCode搭建Node.js服务器开发环境

    使用VsCode搭建Node js服务器开发环境 在进行Node js服务器开发时 一个好的集成开发环境可以帮助您更快地编写代码 并且提高程序的效率 在此推荐安装配置VSCode作为Node js服务器开发环境 下面介绍安装配置过程 Ste
  • ZYNQ ARM核之SCU

    Snoop Control Unit 窥探控制单元 详情见UG585 SCU主要是解决ARM的L1和L2的缓存协调 因为两个processor的缓存是共用的 和AXI总线的ACP存取的 也就是DMA等高速中断需求的外设 SCU 块将两个 C
  • javascript 基础知识之derfer 妙用

    javascript 一般是加载完后立即执行 但是有些时候并不想立即执行 而是等到页面装载完毕时再执行 怎么实现这样的需求呢 答案就是使用
  • DHCP技术原理详解

    今天给大家讲解一下DHCP的原理和技术细节 本文从DHCP基本原理 实现流程和DHCP重启后的流程和租约和续约机制三个方面对DHCP进行了全方位的讲解 基本上涵盖了DHCP的全方面 一 DHCP基本原理 DHCP Dynamic Host
  • JDBC编程——JDBC连接数据库六步骤

    JDBC编程的6步骤 实现数据库连接之前 我们要先理解一下URL 统一资源定位器 是跟数据库进行连接的时候 用来连接到指定远程数据库标识符 可以在该URL中指定连接用户名和密码 同时 对于不同的数据库有不同的标识 URL 统一资源定位符 U
  • sort排序的用法

    https www cnblogs com stones dream p 10183210 html
  • 【IT项目管理】第七章课后习题

    完成作业1 3的要求 使用 project 或其他项目管理工具 1 成本模型如下图 2 为项目每个月制定成本基线如下图 3 已知 Budget at Completion BAC 200000 Planned Value PV 120000
  • 深度学习 图像融合使用笔记 2023 harmonized

    目录 cvpr2023 INR Harmonization即将开源 CDTnet没开源 DCCF 图像滤镜 变色 pil灰度图转opencv
  • java Type 详解

    转自 https blog csdn net gdutxiaoxu article details 68926515 为什么要写这一系列的博客呢 因为在 Android 开发的过程中 泛型 反射 注解这些知识进场会用到 几乎所有的框架至少都