java接口Type的详解

原文地址:http://loveshisong.cn/%E7%BC%96%E7%A8%8B%E6%8A%80%E6%9C%AF/2016-02-16-Type%E8%AF%A6%E8%A7%A3.html

本文主要介绍java中Type接口的来历以及相关的几个接口。
通过这边文章,我们可以了解到与范型相关的几个接口, 对范型的分类有个了解;
还可以了解到Type接口与Class类的关系, 以及Type出现的原因.



反射相关接口

反射相关接口继承关系图

下面就把Type来龙去脉彻底弄清楚

Type

Type是所有类型的父接口, 如原始类型(raw types,对应Class)、 参数化类型(parameterized types, 对应ParameterizedType)、 数组类型(array types,对应GenericArrayType)、 类型变量(type variables, 对应TypeVariable)和基本(原生)类型(primitive types, 对应Class), 子接口有ParameterizedType, TypeVariable<D>, GenericArrayType, WildcardType, 实现类有Class

ParameterizedType

具体的范型类型, 如Map<String, String>
有如下方法:

  1. Type getRawType(): 返回承载该泛型信息的对象, 如上面那个Map<String, String>承载范型信息的对象是Map
  2. Type[] getActualTypeArguments(): 返回实际泛型类型列表, 如上面那个Map<String, String>实际范型列表中有两个元素, 都是String
  3. Type getOwnerType(): 返回是谁的member.(上面那两个最常用)

  
  
  1. public class TestType {
  2. Map < String , String > map ;
  3. public static void main ( String [] args ) throws Exception {
  4. Field f = TestType . class . getDeclaredField ( "map" );
  5. System . out . println ( f . getGenericType ()); // java.util.Map<java.lang.String, java.lang.String>
  6. System . out . println ( f . getGenericType () instanceof ParameterizedType ); // true
  7. ParameterizedType pType = ( ParameterizedType ) f . getGenericType ();
  8. System . out . println ( pType . getRawType ()); // interface java.util.Map
  9. for ( Type type : pType . getActualTypeArguments ()) {
  10. System . out . println ( type ); // 打印两遍: class java.lang.String
  11. }
  12. System . out . println ( pType . getOwnerType ()); // null
  13. }
  14. }

TypeVariable

类型变量, 范型信息在编译时会被转换为一个特定的类型, 而TypeVariable就是用来反映在JVM编译该泛型前的信息.
它的声明是这样的: public interface TypeVariable<D extends GenericDeclaration> extends Type
也就是说它跟GenericDeclaration有一定的联系, 我是这么理解的:
TypeVariable是指在GenericDeclaration中声明的<T>、<C extends Collection>这些东西中的那个变量T、C; 它有如下方法:

  1. Type[] getBounds(): 获取类型变量的上边界, 若未明确声明上边界则默认为Object
  2. D getGenericDeclaration(): 获取声明该类型变量实体
  3. String getName(): 获取在源码中定义时的名字

注意:

  • 类型变量在定义的时候只能使用extends进行(多)边界限定, 不能用super;
  • 为什么边界是一个数组? 因为类型变量可以通过&进行多个上边界限定,因此上边界有多个

  
  
  1. public class TestType < K extends Comparable & Serializable , V > {
  2. K key ;
  3. V value ;
  4. public static void main ( String [] args ) throws Exception {
  5. // 获取字段的类型
  6. Field fk = TestType . class . getDeclaredField ( "key" );
  7. Field fv = TestType . class . getDeclaredField ( "value" );
  8. Assert . that ( fk . getGenericType () instanceof TypeVariable , "必须为TypeVariable类型" );
  9. Assert . that ( fv . getGenericType () instanceof TypeVariable , "必须为TypeVariable类型" );
  10. TypeVariable keyType = ( TypeVariable ) fk . getGenericType ();
  11. TypeVariable valueType = ( TypeVariable ) fv . getGenericType ();
  12. // getName 方法
  13. System . out . println ( keyType . getName ()); // K
  14. System . out . println ( valueType . getName ()); // V
  15. // getGenericDeclaration 方法
  16. System . out . println ( keyType . getGenericDeclaration ()); // class com.test.TestType
  17. System . out . println ( valueType . getGenericDeclaration ()); // class com.test.TestType
  18. // getBounds 方法
  19. System . out . println ( "K 的上界:" ); // 有两个
  20. for ( Type type : keyType . getBounds ()) { // interface java.lang.Comparable
  21. System . out . println ( type ); // interface java.io.Serializable
  22. }
  23. System . out . println ( "V 的上界:" ); // 没明确声明上界的, 默认上界是 Object
  24. for ( Type type : valueType . getBounds ()) { // class java.lang.Object
  25. System . out . println ( type );
  26. }
  27. }
  28. }

GenericArrayType

范型数组,组成数组的元素中有范型则实现了该接口; 它的组成元素是ParameterizedTypeTypeVariable类型,它只有一个方法:

  1. Type getGenericComponentType(): 返回数组的组成对象, 即被JVM编译后实际的对象

  
  
  1. public class TestType < T > {
  2. public static void main ( String [] args ) throws Exception {
  3. Method method = Test . class . getDeclaredMethods ()[ 0 ];
  4. // public void com.test.Test.show(java.util.List[],java.lang.Object[],java.util.List,java.lang.String[],int[])
  5. System . out . println ( method );
  6. Type [] types = method . getGenericParameterTypes (); // 这是 Method 中的方法
  7. for ( Type type : types ) {
  8. System . out . println ( type instanceof GenericArrayType );
  9. }
  10. }
  11. }
  12. class Test < T > {
  13. public void show ( List < String >[] pTypeArray , T [] vTypeArray , List < String > list , String [] strings , int [] ints ) {
  14. }
  15. }
  • 第一个参数List<String>[]的组成元素List<String>ParameterizedType类型, 打印结果为true
  • 第二个参数T[]的组成元素TTypeVariable类型, 打印结果为true
  • 第三个参数List<String>不是数组, 打印结果为false
  • 第四个参数String[]的组成元素String是普通对象, 没有范型, 打印结果为false
  • 第五个参数int[] pTypeArray的组成元素int是原生类型, 也没有范型, 打印结果为false

WildcardType

该接口表示通配符泛型, 比如? extends Number 和 ? super Integer 它有如下方法:

  1. Type[] getUpperBounds(): 获取范型变量的上界
  2. Type[] getLowerBounds(): 获取范型变量的下界

注意:

  • 现阶段通配符只接受一个上边界或下边界, 返回数组是为了以后的扩展, 实际上现在返回的数组的大小是1

  
  
  1. public class TestType {
  2. private List <? extends Number > a ; // // a没有下界, 取下界会抛出ArrayIndexOutOfBoundsException
  3. private List <? super String > b ;
  4. public static void main ( String [] args ) throws Exception {
  5. Field fieldA = TestType . class . getDeclaredField ( "a" );
  6. Field fieldB = TestType . class . getDeclaredField ( "b" );
  7. // 先拿到范型类型
  8. Assert . that ( fieldA . getGenericType () instanceof ParameterizedType , "" );
  9. Assert . that ( fieldB . getGenericType () instanceof ParameterizedType , "" );
  10. ParameterizedType pTypeA = ( ParameterizedType ) fieldA . getGenericType ();
  11. ParameterizedType pTypeB = ( ParameterizedType ) fieldB . getGenericType ();
  12. // 再从范型里拿到通配符类型
  13. Assert . that ( pTypeA . getActualTypeArguments ()[ 0 ] instanceof WildcardType , "" );
  14. Assert . that ( pTypeB . getActualTypeArguments ()[ 0 ] instanceof WildcardType , "" );
  15. WildcardType wTypeA = ( WildcardType ) pTypeA . getActualTypeArguments ()[ 0 ];
  16. WildcardType wTypeB = ( WildcardType ) pTypeB . getActualTypeArguments ()[ 0 ];
  17. // 方法测试
  18. System . out . println ( wTypeA . getUpperBounds ()[ 0 ]); // class java.lang.Number
  19. System . out . println ( wTypeB . getLowerBounds ()[ 0 ]); // class java.lang.String
  20. // 看看通配符类型到底是什么, 打印结果为: ? extends java.lang.Number
  21. System . out . println ( wTypeA );
  22. }
  23. }

再写几个边界的例子:

  • List<? extends Number>, 上界为class java.lang.Number, 属于Class类型
  • List<? extends List<T>>, 上界为java.util.List<T>, 属于ParameterizedType类型
  • List<? extends List<String>>, 上界为java.util.List<java.lang.String>, 属于ParameterizedType类型
  • List<? extends T>, 上界为T, 属于TypeVariable类型
  • List<? extends T[]>, 上界为T[], 属于GenericArrayType类型

它们最终统一成Type作为数组的元素类型


Type及其子接口的来历

泛型出现之前的类型

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

泛型出现之后的类型

泛型出现之后,扩充了数据类型。从只有原始类型扩充了参数化类型、类型变量类型、限定符类型 、泛型数组类型。

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

  • 产生泛型擦除的原因

原始类型和新产生的类型都应该统一成各自的字节码文件类型对象。但是由于泛型不是最初Java中的成分。如果真的加入了泛型,涉及到JVM指令集的修改,这是非常致命的。

  • Java中如何引入泛型

为了使用泛型又不真正引入泛型,Java采用泛型擦除机制来引入泛型。Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的麻烦。但是,一旦编译完成,所有的和泛型有关的类型全部擦除。

  • Class不能表达与泛型有关的类型

因此,与泛型有关的参数化类型、类型变量类型、限定符类型 、泛型数组类型这些类型编译后全部被打回原形,在字节码文件中全部都是泛型被擦除后的原始类型,并不存在和自身类型对应的字节码文件。所以和泛型相关的新扩充进来的类型不能被统一到Class类中。

  • 与泛型有关的类型在Java中的表示

为了通过反射操作这些类型以迎合实际开发的需要,Java就新增了ParameterizedType, TypeVariable<D>, GenericArrayType, WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。

  • 引入Type的原因

为了程序的扩展性,最终引入了Type接口作为Class和ParameterizedType, TypeVariable<D>, GenericArrayType, WildcardType这几种类型的总的父接口。这样可以用Type类型的参数来接受以上五种子类的实参或者返回值类型就是Type类型的参数。统一了与泛型有关的类型和原始类型Class

  • Type接口中没有方法的原因

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: ParameterizedTypeJava 中的一个接口,它表示一个带有类型参数的类型。它可以用来描述复杂的泛型类型,如 List<String> 等。 ParameterizedType 接口有三个方法,分别返回泛型类型的原始类型(raw type)、参数类型(actual type arguments)和所属的类型(owner type)。在 Java 中,我们经常会使用 ParameterizedType 来获取泛型类型的信息,比如反射中的 TypeToken,或者 Json 解析库中的 TypeAdapter。 ### 回答2: ParameterizedTypeJava 中的一个接口,用于表示带有参数化的类型,也就是泛型类型。 在 Java 中,我们可以定义泛型类或泛型方法,在定义的时候可以使用类型参数(type parameter),这些类型参数可以是具体的类型,也可以是泛型类型。例如,我们可以定义一个泛型类List<T>来表示一个列表,其中的类型参数T可以是具体的类型,比如Integer或String,也可以是另一个泛型类型,比如List<Integer>。当我们实例化一个泛型类的对象时,可以指定具体的类型参数,比如List<Integer> list = new ArrayList<>()。 而ParameterizedType 就是用来表示带有参数化的类型的,在 Java 中,它是一个接口,继承自Type接口。ParameterizedType接口提供了一些方法来获取泛型类型的相关信息,比如获取原始类型(raw type)、获取所有的实际类型参数(actual type arguments)等。 通常情况下,当我们通过反射来获取一个泛型类型时,得到的是一个ParameterizedType的实例。通过ParameterizedType接口提供的方法,我们可以获取到该泛型类型的原始类型、实际类型参数等信息。比如,如果我们获取到了一个泛型类List<Integer>的Type类型,我们可以通过判断该Type是否是ParameterizedType类型,然后再通过ParameterizedType接口提供的方法来获取到List的原始类型(即List.class)和类型参数(即Integer.class)。 总之,ParameterizedType 是用来表示带有参数化的类型的接口,在Java中,泛型类或泛型方法的参数是使用类型参数来定义的,而ParameterizedType接口则是用来表示这些泛型类型的。通过ParameterizedType接口提供的方法,我们可以获取到泛型类型的原始类型和类型参数等相关信息。 ### 回答3: ParameterizedTypeJava语言中的一个接口,用于表示参数化类型,也就是带有泛型的类型。在Java中,使用泛型可以让我们编写更加通用、可复用的代码,因此ParameterizedType的作用就是表示这种带有泛型参数的类型。 ParameterizedType接口Type的子接口,它有三个方法: - Type[] getActualTypeArguments():获取参数化类型的实际参数类型,返回一个Type数组。 - Type getRawType():获取参数化类型的原始类型,返回一个Class对象。 - Type getOwnerType():获取参数化类型的拥有者类型,如果该类型是一个嵌套类型,则返回其直接宿主。 举个例子来说,假设我们有一个ArrayList,这个List中的元素是String类型。那么这个ArrayList的类型就可以表示为ParameterizedType,其中getRawType()方法会返回ArrayList的原始类型,即Class对象;getActualTypeArguments()方法会返回长度为1的Type数组,数组中唯一的元素就是String类型;getOwnerType()方法返回null,因为ArrayList没有拥有者类型。 通过ParameterizedType,我们可以在运行时获取到参数化类型的具体信息,这对于编写一些通用的代码非常有用。例如,我们可以通过反射获取泛型类的实际参数类型,并根据这些类型进行相应的处理。 总之,ParameterizedTypeJava语言中用于表示带有泛型参数的类型的接口,它提供了获取实际参数类型、原始类型以及拥有者类型的方法。通过ParameterizedType,我们可以在运行时获取参数化类型的具体信息,从而编写更加通用、可复用的代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值