Type的子接口详解(源码解析)

以下是源码中对Type的注释:Type是Java中所有类型的常见的超接口,在编程语言中这些包括原始类型,参数化的类型,数组类型,类型变量和原始类型。 Class在一定程度上挽救了擦除的类型信息,我们就可以通过这几个接口来获取被擦除的类型参数信息,这几个接口无非就是对类型参数的一个分类,通过它们提供的一些方法,我们可以逐步的获取到最原始的类型参数的Class对象。

Type的直接子接口
1.ParameterizedType: 表示一种参数化的类型,比如Collection,即普通的泛型。  形如:Object<T, K>,即常说的泛型
2.TypeVariable:是各种类型变量的公共父接口, 描述类型,表示泛指任意或相关一类类型,也可以说狭义上的泛型(泛指某一类类型),一般用K、V、E等。  
3.GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型,泛型数组,描述的是形如:A<T>[]或T[]类型。List<>[],T[]这种。 
4.WildcardType:代表一种通配符类型表达式,泛型表达式,但是在Java中并没有WildcardType类型,也可以说是,限定性的泛型,形如:? extends classA、?super classB、? super T这样的通配符表达式。

Type及其子接口的来历
一. 泛型出现之前的类型: 没有泛型的时候,只有所谓的原始类型。此时,所有的原始类型都通过字节码文件类Class类进行抽象。Class类的一个具体对象就代表一个指定的原始类型。

二. 泛型出现之后的类型 :泛型出现之后,扩充了数据类型。从只有原始类型扩充了参数化类型、类型变量类型、泛型限定的的参数化类型 (含通配符+通配符限定表达式)、泛型数组类型。

三. 与泛型有关的类型不能和原始类型统一到Class的原因
(1) 产生泛型擦除的原因 :本来新产生的类型+原始类型都应该统一成各自的字节码文件类型对象。但是由于泛型不是最初Java中的成分。如果真的加入了泛型,涉及到JVM指令集的修改,这是非常致命的。
(2) Java中如何引入泛型 :为了使用泛型的优势又不真正引入泛型,Java采用泛型擦除的机制来引入泛型。Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的麻烦。但是,一旦编译完成,所有的和泛型有关的类型全部擦除。
(3) Class不能表达与泛型有关的类型 :因此,与泛型有关的参数化类型、类型变量类型、泛型限定的的参数化类型 (含通配符+通配符限定表达式)、泛型数组类型这些类型全部被打回原形,在字节码文件中全部都是泛型被擦除后的原始类型,并不存在和自身类型一致的字节码文件。所以和泛型相关的新扩充进来的类型不能被统一到Class类中。
(4) 与泛型有关的类型在Java中的表示 :为了通过反射操作这些类型以迎合实际开发的需要,Java就新增了ParameterizedType,GenericArrayType,TypeVariable 和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。
(5) Type的引入 :统一与泛型有关的类型和原始类型Class

引入Type的原因 为了程序的扩展性,最终引入了Type接口作为Class,ParameterizedType,GenericArrayType,TypeVariable和WildcardType这几种类型的总的父接口。这样实现了Type类型参数接受以上五种子类的实参或者返回值类型就是Type类型的参数。

Type接口中没有方法的原因 从上面看到,Type的出现仅仅起到了通过多态来达到程序扩展性提高的作用,没有其他的作用。因此Type接口的源码中没有任何方法。


以下是 ParameterizedType 的源码:
/**
*ParameterizedType代表一个参数化的类型,比如<字符串>集合。
*
* 创建一个参数化类型第一次需要通过反射方法指定在这个包中。当创建参数化类型p,p实例化泛型类型声明是解决,和所有类型参数p创建递归。看到{数组。TypeVariable TypeVariable }详情类型变量的创建过程。重复创建一个参数化的类型没有影响。
*
* 实例实现该接口的类必须实现一个equals()方法,把任意两个实例共享相同的泛型类型声明和类型参数平等。
**/
public interface ParameterizedType extends Type {
/**
*返回一个数组,该数组表示该类型的实际类型参数。
*注意到在某些情况下,如果该类型表示嵌套在参数化类型中的非参数化类型,返回的数组是空的。
*返回一个数组,该类型代表实际类型参数的对象
* 如果任何的实际类型参数指向一个不存在的类型声明@抛出 typenotpresentexception
*如果任何实际的类型参数是一个参数化的类型不能被实例化的任何理由 @抛出 malformedparameterizedtypeexception
* @since 1.5
*/
Type[] getActualTypeArguments(); //1.获得实际类型

/**
*返回表示该类型的类或接口的<类型>。
*返回表示该类型的类或接口的对象类型
* @since 1.5
*/
Type getRawType(); //2.获得<>前面实际类型

/**
*返回一个表示该类型为成员的类型的对象类型。例如,如果这个类型是“O<T>.I<S>”,则返回一个“O<T>”的表示。
*如果该类型是顶级类型,则返回< null >。
*返回类型为该类型为成员的类型的对象。如果该类型是顶级类型,则返回 < null >
* @抛出typenotpresentexception 如果业主型指的是一个不存在的类型声明
* @抛出malformedparameterizedtypeexception 如果业主型指的是一个参数化的类型不能被实例化的任何理由
* @since 1.5
*/
Type getOwnerType(); //3.如果这个类型是某个类型所属,获得这个所有者类型,否则返回null
}
1.getActualTypeArguments   :获得参数化类型中<>里的类型参数的类型,因为可能有多个类型参数,例如Map<K, V>,所以返回的是一个Type[]数组。【注意】无论<>中有几层<>嵌套,这个方法仅仅脱去最外层的<>,之后剩下的内容就作为这个方法的返回值,所以其返回值类型不一定。 也就是说会获得实际的参数类型的数组。
2.getRawType   返回最外层<>前面那个类型,即Map<K ,V>的Map,即得到 声明此参数化类型的类。
3.getOwnerType   :获得这个类型的所有者的类型。 如果此类型为 O<T>.I<S>,则返回 O<T> 的表示形式。如果此类型为顶层类型,则返回 null。

{1}. 对于ArrayList>,通过getActualTypeArguments()返回之后,脱去最外层的<>之后,剩余的类型是ArrayList。因此对这个参数的返回类型是ParameterizedType。
{2}. 对于ArrayList,通过getActualTypeArguments()返回之后,脱去最外层的<>之后,剩余的类型是E。因此对这个参数的返回类型是TypeVariable。
{3}. 对于ArrayList,通过getActualTypeArguments()返回之后,脱去最外层的<>之后,剩余的类型是String。因此对这个参数的返回类型是Class。
{4}. 对于ArrayListextends Number>,通过getActualTypeArguments()返回之后,脱去最外层的<>之后,剩余的类型是? ExtendsNumber。因此对这个参数的返回类型是WildcardType。
{5}. 对于ArrayList,通过getActualTypeArguments()返回之后,脱去最外层的<>之后,剩余的类型是E[]。因此对这个参数的返回类型是GenericArrayType。
所以,可能获得各种各样类型的实际参数,所以为了统一,采用直接父类数组Type[]进行接收。

以下是 TypeVariable的源码:
/**
* TypeVariable是常见的超接口类型变量的类型。第一次创建一个类型变量需要通过反射方法,指定在这个包中。如果一个类型变量t是引用的类型T(i.e、类、接口或注释类型),并且是T n封闭类的声明,然后创建T需要解决 T的封闭类,因为我= 0到n、包容。创建类型变量不能导致其边界的创建。重复创建类型变量没有影响。
*
* 在运行时可以实例化多个对象来表示给定的类型变量。即使只创建一次类型变量,但这并不意味着需要缓存表示类型变量的实例。然而,所有的实例代表一个类型的变量必须equal()彼此。因此,类型变量的用户不能依赖于实现此接口的类实例的标识。
*
* @param < D >的泛型类型声明宣布底层类型变量。
*
* @since 1.5
*/

public interface TypeVariable<D extends GenericDeclaration> extends Type {
/**
* 返回一个数组类型的对象代表这类变量的上界。注意,如果没有显式声明上限,上限是对象。
*对于每一个上界B:如果B是一个参数化的类型或类型变量,它是创建的,(为创建过程的细节参见参数化类型)。否则,B是已解决的。
* @throws TypeNotPresentException如果任何范围指的是一个不存在的类型声明
  * @throws MalformedParameterizedTypeException如果任何范围指的是参数化类型,不能因为任何原因被实例化
  * @return数组类型的代表上界(s)这种类型的变量
*/
Type[] getBounds(); //获得泛型的上限,若未明确声明上边界则默认为Object

/**
*返回表示泛型声明宣布的这种GenericDeclaration对象变量。
  * @return泛型声明宣布这种类型变量。
* @since 1.5
*/
D getGenericDeclaration(); //获取声明该类型变量实体(即获得类、方法或构造器名)

/**
*返回这类变量的名称,因为它发生在源代码中。
  * @return这种类型变量的名称,因为它出现在源代码
*/
String getName(); //获得名称,即K、V、E之类名称
}
大家可能会发现这个不仅继承了Type还继承了 GenericDeclaration接口该接口用来定义哪些对象上是可以声明(定义)范型变量,所谓范型变量就是<E extends List>或者<E>, 也就是TypeVariable<D>这个接口的对应的对象,TypeVariable<D>中的D是extends GenericDeclaration的,用来通过范型变量反向获取拥有这个变量的GenericDeclaration。 目前实现GenericDeclaration接口的类包括Class, Method, Constructor,也就是说只能在这几种对象上进行范型变量的声明(定义)。GenericDeclaration的接口方法getTypeParameters用来逐个获取该GenericDeclaration的范型变量声明。

//1.在类(Class)上声明(定义)类型变量class A<T>{ T a;}A<String> as = new A<String>(); //之后这里可用任意类型替换T,集合就是泛型的一个典型运用
//2.在方法上声明(定义) public <E> void test(E e){} //方法上,类型变量声明(定义)不是在参数里边,而且必须在返回值之前,static等修饰后

//3.声明(定义)在构造器上 public <K> A(K k){}

注意:类型变量声明(定义)的时候不能有下限(既不能有super),否则编译报错。因为T extends classA表示泛型有上限classA,当然可以,因为这样,每一个传进来的类型必定是classA(具有classA的一切属性和方法),但若是T super classA,传进来的类型不一定具有classA的属性和方法,当然就不适用于泛型。

1.getBounds  :获得该类型变量的上限(上边界),若无显式定义(extends),默认为Object,类型变量的上限可能不止一个,因为可以用&符号限定多个(这其中有且只能有一个为类或抽象类,且必须放在extends后的第一个,即若有多个上边界,则第一个&后必为接口)。

2.getGenericDeclaration  :获得声明(定义)这个类型变量的类型及名称,即如: class com.xxx.xxx.classA 或 public void com.fcc.test.Main.test(java.util.List) 或 public com.fcc.test.Main()
3.getName  :获得这个类型变量在声明(定义)时候的名称


以下是GenericArrayType 的源码:

/**
* GenericArrayType代表数组类型的组件类型参数化的类型或类型变量。
* @since 1.5
*/
public interface GenericArrayType extends Type {
/**
*返回一个表示组件类型的类型对象的数组。这个方法创建数组的组件类型。看到{数组的声明。ParameterizedType ParameterizedType }创建过程的参数化的语义类型,看看{数组。TypeVariable TypeVariable }类型变量的创建过程。
*
* @return表示组件类型的类型对象的数组
  * @throws TypeNotPresentException如果底层数组类型的组件类型指的是一个不存在的类型声明
  * @throws MalformedParameterizedTypeException如果底层数组类型的组件类型指的是一个参数化的类型,不能因为任何原因被实例化
*/
Type getGenericComponentType(); //获得这个数组元素类型,即获得:A<T>(A<T>[])或T(T[])
}
GenericArrayType,泛型数组,描述的是ParameterizedType类型以及TypeVariable类型数组,即形如:classA<T>[][]、T[]等,是Type的子接口。 <>不能出现在数组的初始化中,即new数组之后不能出现<>,否则javac无法通过。但是作为引用变量或者方法的某个参数是完全可以的。获取泛型数组中元素的类型。
1. getGenericComponentType  获取泛型数组中元素的类型,要注意的是: 无论从左向右有几个[]并列,这个方法仅仅脱去最右边的[]之后剩下的内容就作为这个方法的返回值。

{1}. 对于String[],通过getComponentType()返回之后,脱去最右边的[]之后,剩余的类型是String。因此对这个参数的返回类型是Class
{2}. 对于E[],通过getComponentType()返回之后,脱去最右边的[]之后,剩余的类型是E。因此对这个参数的返回类型是TypeVariable
{3}. 对于ArrayList[],通过getComponentType()返回之后,脱去最右边的[]之后,剩余的类型是ArrayList。因此对这个参数的返回类型是ParameterizedType
{4}. 对于E[][],通过getComponentType()返回之后,脱去最右边的[]之后,剩余的类型是E[]。因此对这个参数的返回类型是GenericArrayType





以下是WildcardType 的源码:
/**
* WildcardType代表一个通配符类型表达式,如?、?extends Number、?super Integer.
* @since 1.5
*/
public interface WildcardType extends Type {
/**
* 返回一个数组类型的对象代表上界(s)这种类型的变量。注意,如果没有显式声明上限,上限是对象。
*
* 对于每一个上界B:如果B是一个参数化的类型或类型变量,它是创建,为创建过程的细节参数化types。否则, B 是被解决的.
*
* @return数组类型代表上界(s)这种类型的变量
  * @throws TypeNotPresentException如果任何范围指的是一个不存在的类型声明
  * @throws MalformedParameterizedTypeException如果任何范围指的是参数化类型,不能因为任何原因被实例化
*/
Type[] getUpperBounds(); //获得泛型表达式上界(上限)

/**
* 返回一个数组类型的对象代表下界(s)这种类型的变量。注意,如果没有显式声明下限,下限是空的类型。在本例中,返回一个零长度数组。
*
* 为每一个下界B:如果B是一个参数化的类型或类型变量,创建它,(见{数组。ParameterizedType ParameterizedType }为创建过程的细节参数化类型)。否则,B是被解决的。
*
* @return数组类型代表下界(s)这种类型的变量
  * @throws TypeNotPresentException如果任何范围指的是一个不存在的类型声明
  * @throws MalformedParameterizedTypeException如果任何范围指的是参数化类型,不能因为任何原因被实例化
*/
Type[] getLowerBounds(); //获得泛型表达式下界(下限)
// 一个或多个?语言规范;目前只有一个,但这个API允许泛化。
}

1.getUpperBounds  获取泛型表达式上界,根据API的注释提示:现阶段通配符表达式仅仅接受一个上边界或者下边界,这个和定义类型变量时候可以指定多个上边界是不一样。但是API说了,为了保持扩展性,这里返回值类型写成了数组形式。实际上现在返回的数组的大小就是1,通配符?指定多个上边界或者下边界现在是会编译出错的(jdk1.7是这样的,至于7及以后就不知道了)。 
2.getLowerBounds  :获取泛型表达式下界。

根据上面API的注释提示:现阶段通配符表达式仅仅接受一个上边界或者下边界,这个和定义类型变量时候可以指定多个上边界是不一样。但是API说了,为了保持扩展性,这里返回值类型写成了数组形式。实际上现在返回的数组的大小就是1,获取通配符表达式对象的泛型限定的上边界的类型,例如下面的方法:
{1}. public static voidprintColl(ArrayListextends ArrayList> al){}
通配符表达式是:? extendsArrayList,这样 extends后面是?的上边界,这个上边界是ParameterizedType类型。
{2}. public static  voidprintColl(ArrayListextends E> al){}
通配符表达式是:? extends E,这样 extends后面是?的上边界,这个上边界是TypeVariable类型
{3}.public static  voidprintColl(ArrayListextends E[]> al){}
通配符表达式是:? extends E[],这样 extends后面是?的上边界,这个上边界是GenericArrayType类型
{4}.public static  voidprintColl(ArrayListextends Number> al){}
通配符表达式是:? extends Number,这样 extends后面是?的上边界,这个上边界是Class类型
最终统一成Type作为数组的元素类型。

猜你喜欢

转载自blog.csdn.net/ZytheMoon/article/details/79369924
今日推荐