概述

在JDK1.5之前之一原始类型。此时,所有的原始类型都通过字节码文件类Class进行抽象。Class类的一个具体对象就代表一个指定的原始类型。
JDK1.5加入了泛型类,扩充了数据类型,从只有原始类型基础上扩充了 参数化类型、类型变量类型、通配符类型、泛型数组类型。
Type是Java语言中所有类型(Class)的公共父接口。

Type类关系图


从做到右依次是:Class(原始/基本类型):实现了Type接口,GenericArrayType(泛型数组类型),ParameterizedType(参数化类型),WildcardType(通配符类型),TypeVariable(类型变量)。

  • Class:不仅包括我们平常所指的类、枚举、数组、注解,还包括基本类型int、float等等。
  • TypeVariable:比如List 中的T等。
  • WildcardType:也叫做泛型表达式类型,例如List<? extends Number> 这种。
  • ParameterizedType:就是我们平常所用到的泛型List、Map(注意和TypeVariable的区别,参数化类型表示的是List这样的一个整体而不是T)。
  • GenericArrayType:泛型数组类型,并不是我们工作中所使用的数组String[]、Byte[],这些都是Class,而是带有泛型的数组,即T[]。

Type接口本身算是一个标记接口,不提供任何需要复写的方法。

ParameterizedType 参数化类型

参数化类型,即平常用的泛型;例如:List、Map<K,V>等带有参数化的对象。
如何理解呢?
一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化(比如:List,T就是类型参数),类似于方法中的参数变量,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

public interface ParameterizedType extends Type {// 获取类型内部的参数化类型,比如Map<K,V>里面的K,V类型;// 注意该方法只返回最外层的<>中的类型,无论该<>内有多少个<>。Type[] getActualTypeArguments();// 类的原始类型,一般都是ClassType getRawType();// 获取所有者类型(只有内部类才有所有者,比如Map.Entry的所有者就是Map),如果不是内部类,返回null。Type getOwnerType();
}

具体用法

@Slf4j
public class ParameterizedTypeTest {private Map<String, ParameterizedTypeTest> map;private Set<String> setStr;private Class<?> clz;private Holder<String> holder;private List<String> listStr;private ArrayList<String> arrayList;private Map.Entry<String, String> entry;// 非参数化类型private String str;private Integer i;private Set set;private List list;static class Holder<V> {}public static void main(String[] args) {try {// 拿到所有的字段Field[] fields = ParameterizedTypeTest.class.getDeclaredFields();Arrays.stream(fields).forEach(item -> {if (item.getGenericType() instanceof ParameterizedType) {item.setAccessible(true);ParameterizedType parameterizedType = (ParameterizedType) item.getGenericType();log.info("{} :", item.getName());log.info("getActualTypeArguments: {}", Arrays.asList(parameterizedType.getActualTypeArguments()));log.info("getRawType: {}", parameterizedType.getRawType());log.info("getOwnerType: {}", parameterizedType.getOwnerType());} else {log.info("{} is not ParameterizedType", item.getName());}});} catch (Exception e) {log.info("exception:", e);}}
}

输出:

map :
getActualTypeArguments: [class java.lang.String, class com.study.concurrentprogramming.genericiy.ParameterizedTypeTest]
getRawType: interface java.util.Map
getOwnerType: null
setStr :
getActualTypeArguments: [class java.lang.String]
getRawType: interface java.util.Set
getOwnerType: null
clz :
getActualTypeArguments: [?]
getRawType: class java.lang.Class
getOwnerType: null
holder :
getActualTypeArguments: [class java.lang.String]
getRawType: class com.study.concurrentprogramming.genericiy.ParameterizedTypeTest$Holder
getOwnerType: class com.study.concurrentprogramming.genericiy.ParameterizedTypeTest
listStr :
getActualTypeArguments: [class java.lang.String]
getRawType: interface java.util.List
getOwnerType: null
arrayList :
getActualTypeArguments: [class java.lang.String]
getRawType: class java.util.ArrayList
getOwnerType: null
entry :
getActualTypeArguments: [class java.lang.String, class java.lang.String]
getRawType: interface java.util.Map$Entry
getOwnerType: interface java.util.Map
str is not ParameterizedType
i is not ParameterizedType
set is not ParameterizedType
list is not ParameterizedType

先看后面几个,发现即使是List,但是我们没给与泛型,它也不是ParameterizedType参数化类型,因此如果想成为泛型类型必须要指定泛型参数才行。

TypeVariable 类型变量

泛型信息在编译时会被转换成一个特定的类型,而TypeVariable就是用来反映在JVM编译该泛型前的信息。(通俗的讲,TypeVariable就是我们常用的List 、Map<K,V>中的T,K这种泛型变量)。
还可以对类型变量加上extends限定,这样会有类型变量对应的上限;值得注意的是,类型变量的上限可以有多个,必须使用&连接,例如:
List<T extends Number & Serializable>,其中&后必须是接口:

public interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement {// 类型变量对应的上边界,如果没有指定上限,返回Object 可以有多个Type[] getBounds();// 获取类型变量所在类的Type,比如TypeVariableTest<T>类,getGenericDeclaration()得到的就是TypeVariableTest。D getGenericDeclaration();// 获取类型变量在源码中定义的名称String getName();// JDK8新增的,获取注解类型的上限数组AnnotatedType[] getAnnotatedBounds();
}

具体用法

public class TypeVariableTest<K extends Number, T> {// K有指定上边界 Numberprivate K key;// T没有指定上边界,其默认上边界为Objectprivate T value;public static void main(String[] args) {TypeVariable<Class<TypeVariableTest>>[] typeParameters = TypeVariableTest.class.getTypeParameters();for (TypeVariable<Class<TypeVariableTest>> type : typeParameters) {int index = type.getBounds().length - 1;// 输出上边界System.out.println("---getBounds()-- " + type.getBounds()[index]);// 输出名称System.out.println("---getName()-- " + type.getName());// 输出所在的类的类型System.out.println("---getGenericDeclaration()-- " + type.getGenericDeclaration());}}
}

输出:

---getBounds()-- class java.lang.Number
---getName()-- K
---getGenericDeclaration()-- class com.study.concurrentprogramming.genericiy.TypeVariableTest
---getBounds()-- class java.lang.Object
---getName()-- T
---getGenericDeclaration()-- class com.study.concurrentprogramming.genericiy.TypeVariableTest

GenericArrayType 泛型数组类型

描述的是形如:A[](参数化类型数组)或者T[](类型变量数组)。
它的组成元素是ParameterizedType或TypeVariable类型。

无论从左向右右几个[]并列,这个方法仅仅脱去最右边的[]之后剩下的内容就作为这个方法的返回值。

public interface GenericArrayType extends Type {// 返回泛型数组中成员类型,即List<String>[]中的List<String>Type getGenericComponentType();
}

具体用法

public class GenericArrayTypeTest<T> {// 泛型数组类型private T[] value;private List<String>[] lists;// 非泛型数组类型private List<String> list;private T singleValue;public static void main(String[] args) {Field[] declaredFields = GenericArrayTypeTest.class.getDeclaredFields();Arrays.stream(declaredFields).forEach(field -> {field.setAccessible(true);// 输出当前变量是否为GenericArrayType类型System.out.println("Field: " + field.getName() + "; instanceof GenericArrayType: "+ (field.getGenericType() instanceof GenericArrayType));if (field.getGenericType() instanceof GenericArrayType) {// 输出泛型类型System.out.println("Field: " + field.getName() + "; getGenericComponentType():"+ ((GenericArrayType) field.getGenericType()).getGenericComponentType());}field.setAccessible(false);});}
}

输出:

Field: value; instanceof GenericArrayType: true
Field: value; getGenericComponentType():T
Field: lists; instanceof GenericArrayType: true
Field: lists; getGenericComponentType():java.util.List<java.lang.String>
Field: list; instanceof GenericArrayType: false
Field: singleValue; instanceof GenericArrayType: false

WildCardType 通配符类型

表示通配符类型,比如<?>,<? extends Number>等。

如果没有指定上边界,则默认Object,如果没有指定下边界,则默认为String。

public interface WildcardType extends Type {// 获得泛型表达式上边界,表达式中使用extendsType[] getUpperBounds();// 或者泛型表达式的下边界,表达式中使用superType[] getLowerBounds();
}

具体用法

public class WildcardTypeTest {// 使用通配符类型参数的方法public void testWildcardType(List<? extends OutputStream> outputStreams, List<? super InputStream> inputStreams,List<Integer> list, InputStream inputStream) {}public static void main(String[] args) {// 获取WildcardTypeTest类的所有方法(本例中是testWildcardType方法)Method[] declaredMethods = WildcardTypeTest.class.getDeclaredMethods();for (Method method : declaredMethods) {System.out.println("method name: " + method.getName());// 获取方法的所有参数类型Type[] genericParameterTypes = method.getGenericParameterTypes();for (Type type : genericParameterTypes) {System.out.println("type: " + type.toString());// 如果不是参数化类型则直接continueif (!(type instanceof ParameterizedType)) {continue;}// 将当前类型强转为参数化类型并获取实际参数(即含有通配符的泛型类型)Type actualTypeArgument = ((ParameterizedType) type).getActualTypeArguments()[0];// 输出其是否为通配符类型System.out.println("type instanceof WildcardType: "+ (actualTypeArgument instanceof WildcardType));if (actualTypeArgument instanceof WildcardType) {int lowIndex = ((WildcardType) actualTypeArgument).getLowerBounds().length - 1;int upperIndex = ((WildcardType) actualTypeArgument).getUpperBounds().length - 1;// 输出上边界与下边界System.out.println("getLowerBounds(): " +(lowIndex >= 0 ? ((WildcardType) actualTypeArgument).getLowerBounds()[lowIndex] : "String")+ ";getUpperBounds(): " +(upperIndex >= 0 ? ((WildcardType) actualTypeArgument).getUpperBounds()[upperIndex] : "Object"));}}}}
}

输出:

method name: main
type: class [Ljava.lang.String;
method name: testWildcardType
type: java.util.List<? extends java.io.OutputStream>
type instanceof WildcardType: true
getLowerBounds(): String;getUpperBounds(): class java.io.OutputStream
type: java.util.List<? super java.io.InputStream>
type instanceof WildcardType: true
getLowerBounds(): class java.io.InputStream;getUpperBounds(): class java.lang.Object
type: java.util.List<java.lang.Integer>
type instanceof WildcardType: false
type: class java.io.InputStream

表达式中没有指定上限,默认都是有上限class java.lang.Object,但是没有默认下线。

泛型中使用&(并且)操作符

我们有时候可以看到泛型搭配上 & 的使用方式,比如:

public <R extends Enum<R> & BaseIntEnum> List<R> parse2Enums(...){...}

说明一点:& 不能用于 ? 通配符上(因为通配符不能放在泛型的申明上),& 只能放在泛型的声明上,比如类似这种:

    // 泛型类上申明,约束泛型类变量class WildcardTypeT<T extends Comparable<T> & List<T> & Serializable> {}// 方法上申明public <R extends Enum<R> & Serializable> List<R> parse2Enums(){}

需要注意的是,&后面只能放置接口,不能是具体的类型,即使是Object也不行。
因此当需要多重约束的时候,可以使用&操作符,但是它不能用于super上 ,因为java有规定:

    // 合法的class A<T extends Number & Serializable> {}// 不合法的class B<T super Number & Serializable>

原因参考:https://stackoverflow.com/questions/37411256/why-super-keyword-in-generics-is-not-allowed-at-class-level

与泛型有关的类型不能和原始类型统一到Class的原因

产生泛型擦除的原因

  • 原始类型和新产生的类型都应该统一成各自的字节码文件类型对象。但是由于泛型不是最初Java中的成分,如果真的加入了泛型,涉及到JVM指令集的修改,这是非常致命的(简单的说就是Java要向下兼容,所以它的泛型是个加东西,只是语法层面的技术)。
  • 泛型仅仅存在于编译阶段,当在JVM运行的过程中,于泛型相关的信息将会被擦除,如果List于List都将会在运行时被擦除成List这个类型。而类型擦除机制存在的原因正是因为如果在运行时存在泛型,那么将要修改JVM指令集,这是非常致命的。
  • 我们其实可以通过定义类的方式,在类信息中保留泛型信息,从而在运行时获得这些泛型信息。
    简而言之,Java的泛型擦除是有范围的,即类定义中的泛型是 不会 被擦除的。

引入Type的原因

原始类型会生成字节码文件对象,而泛型相关的类型不会生成与其相对应的字节码文件(因为泛型类型将会被擦除),因此,无法将泛型相关的新类型与class相统一。
因此为了程序的扩展性以及为了开放需要去反射操作这些类型,就引入了Type这个类型,并且新增了ParameterizedType、TypeVariable、GenericArrayType、WildcardType四个表示泛型相关的类型,再加上Class,这样就可以用Type类型的的参数来接收以上5种子类的实参或者返回值类型就是Type类型的参数。统一了与泛型有关的类型和原始类型Class。

总结

  • Type是JDK5开始引入的,其引入主要是为了泛型,没有泛型之前,只有所谓的原始类型。此时,所有的原始类型都通过字节码文件类Class类进行抽象。Class类的一个具体对象就代表一个指定的原始类型。
  • 泛型出现之后,也就扩充了数据类型。从只有原始类型扩充了参数化类型、类型变量类型、泛型数组类型,也就是Type的子接口。
  • 那为什么没有统一到Class下,而是增加了一个Type呢?(Class是种类的意思,Type是类型的意思)。
  1. 是为了程序的扩展性,最终引入了Type接口作为Class、ParameterizedType、GenericArrayType、TypeVariable和WildcardType这几种类型的总的父接口。
  2. 这样实现Type类型参数接受以上五种子类实参或者返回值类型就是Type类型的参数,Type作为媒介统一处理原始类型和泛型相关类型。

List<T ? extends String>[]:这里List<T ? extends String>就是ParameterizedType,T是TypeVariable,T ? extends String是WildcardType(注意,WildcardType不是java类型,而是一个表达式),整个List<T ? extends String>[]就是GenericArrayType。

参考

https://cloud.tencent.com/developer/article/1497707
https://juejin.cn/post/6844903597977632776

Java中的Type类型详解相关推荐

  1. 1.Type类型详解

    文章目录 1.Type类型 1.1 什么是Type? 1.2 Type的用处 1.3 ParmeterizedType(参数化类型)的作用 1.3.1 测试ParmeterizedType 1.3.2 ...

  2. Java中的运算符——赋值运算符详解

    赋值运算符主要是这几种:=.+=.-=.*=./=.%=,其它和二进制相关的内容这里就不详细解释了.赋值类的运算符包括基本赋值运算符(=)和扩展的赋值运算符(+=.-=.*=./=.%=).我们来看一 ...

  3. Java中的static关键字详解

    ** Java中的static关键字详解 ** 在一个类中定义一个方法为static,即静态的,那就是说无需本类的对象就可以调用此方法.调用一个静态方法就是 "类名.方法名" ,静 ...

  4. Java中的main()方法详解

    源文作者:leizhimin    源文链接:http://lavasoft.blog.51cto.com/62575/53263 源文作者版权申明: 版权声明:原创作品,允许转载,转载时请务必以超链 ...

  5. mysql binlog event_MySQL binlog中的事件类型详解

    MySQL binlog记录的所有操作实际上都有对应的事件类型的,譬如STATEMENT格式中的DML操作对应的是QUERY_EVENT类型,ROW格式下的DML操作对应的是ROWS_EVENT类型. ...

  6. java中list和map详解

    java中list和map详解 一.概叙 List , Set, Map都是接口,前两个继承至Collection接口,Map为独立接口, List下有ArrayList,Vector,LinkedL ...

  7. stringtokenizer java_基于Java中的StringTokenizer类详解(推荐)

    StringTokenizer是字符串分隔解析类型,属于:Java.util包. 1.StringTokenizer的构造函数 StringTokenizer(String str):构造一个用来解析 ...

  8. Burp suite—Intruder中Attack Type模式详解

    Burp suite-Intruder中Attack Type模式详解 提要:Burp suite集成工具中的Intruder模块在日常的使用的安全测试中会经常频繁使用,Intruder中的攻击类型( ...

  9. Java中的运算符——逻辑运算符详解

    逻辑运算符主要包括逻辑与( & ),逻辑或( | ),逻辑异或( ^ ),短路与( && ),短路或( || ).所有逻辑运算符的特点是操作数都是布尔类型,并且最终的运算结果也 ...

最新文章

  1. 10大高性能开发宝石,我要消灭一半程序员!
  2. java通过按钮打开新窗口_如何在java程序中,当点击一个按钮后,关闭当前窗口,开启一个新的窗口。...
  3. GDCM:无效的DICOM文件的测试程序
  4. Ubuntu 9.04下安装source navigator---linux下看代码工具
  5. AppiumDriver java部分api
  6. android daemon 程序,(转)Android App Daemon
  7. 仍然报错_only_full_group_by配置,竟让所有应用报错?
  8. 实现API接口调用--来源阿里云大学-归档
  9. Python邻接矩阵邻接表转换
  10. LG电子成功进行太赫兹频段6G无线信号传输,距离超过100米
  11. Html中 table,list等表格 中 js 的 Checkbox全选,反选,单选,获取数据选中行 的写法
  12. logstash关于date时间处理的几种方式总结
  13. FFmpeg代码实现视频转jpg图片
  14. 新的开始——参加培训
  15. 富文本编辑器 可全屏可粘贴(只能单独粘贴图片或文字)
  16. 计算机应用基础2004版,计算机应用基础试题2004年6月
  17. 腾讯、百度、高德、谷歌、天地图地图瓦片
  18. g代码生成器 源代码_如何生成源代码?
  19. 索尼的hlg是什么_hlg(索尼hlg和slog的区别)
  20. Android网络框架Volley项目实战-刘桂林-专题视频课程

热门文章

  1. DataGrip下载与安装2022版教程注册码DataGrip使用配置
  2. 网络交换技术,以太网工作方式,交换机工作原理以及STP,RSTP
  3. Onvif协议学习:13、遮挡报警
  4. watching memory
  5. 对地理没兴趣?看我大Python实现地球仪!太完美了!
  6. 将excel中的多个工作表sheet合成一个工作表
  7. Linux资源限制命令—ulimit
  8. 1248:Dungeon Master/P2360 地下城主
  9. 为什么我的JSP文件使用不了request.getParameter()方法
  10. 使用J2ME发送手机短信