概述
在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();
// 类的原始类型,一般都是Class
Type 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有指定上边界 Number
private K key;
// T没有指定上边界,其默认上边界为Object
private 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 {
// 获得泛型表达式上边界,表达式中使用extends
Type[] getUpperBounds();
// 或者泛型表达式的下边界,表达式中使用super
Type[] 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());
// 如果不是参数化类型则直接continue
if (!(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>
与泛型有关的类型不能和原始类型统一到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是类型的意思)。
- 是为了程序的扩展性,最终引入了Type接口作为Class、ParameterizedType、GenericArrayType、TypeVariable和WildcardType这几种类型的总的父接口。
- 这样实现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
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/100292.html