欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java 反射 泛型机制 获得泛型的实际类型(一)

程序员文章站 2022-05-13 11:12:13
...
    java运用反射机制能够读取和调用加载到内存中的java程序(Class,Field,Method,Array等)。但是,如果属性或方法使用了泛型来声明数据类型,那么能否读取java程序所指定的泛型具体是什么呢?
    在网上看到过一个牛人在百度知道里对一个人的回答,他的意思大概是泛型只在编译的时候用于类型的检查,java程序加载进内存后,泛型就不存在了,而jdk的反射是模拟java运行时的环境读取和调用程序,因此,不能获得泛型的实际类型。牛人告诫道不能把泛型的功能想得太强大了,如果要获得泛型的实际类型,只有在运行时泛型被指定后才能读取。例如如下方法可以读取泛型实例化之后具体是什么类型的,但是该方法只有在java类被实例化之后才能调用。
      public class GenericClass<T extends List<Number> >{
          private T t;
          private List<Contract> contracts;

          public Class getActualClass(){
                 return t.getClass();
          }

      }
     牛人的观点很有道理。再例如,我们在用反射读取调用getDeclaraedMethod的时候,传入的参数类型列表就不能使用泛型。
     但是,有时候,比如属性 private List<Contract> contracts;我们就想知道List所指定的泛型是什么,然后才能给contracts属性设置一个vlalue。这时候该怎样读取属性呢?
     java的反射机制还有一套Type接口,该接口的实现类和子接口向我们展示了java的泛型在运行时是怎么实现的。访问这些接口可以在运行时读取关于泛型的数据,因此也不能说泛型在运行时就没有了,而是交给其他对象来处理了。
    Class类直接实现了Type接口,但是Type的实现类不止一个,换句话说,Class仅仅是Type的一种,如果我们在运行时通过  type instanceof Class 返回true 则可以将type类型强转为Class实例,即 Class cla = (Class)type; 然后就可以用该Class实例构造对象调用方法了。换言之,泛型是class之外的Type,我们只要用反射访问各种Type,直到某个Type被验证是Class类型的,那我们就能找到我们想要的运行时Class了。
    那么Class之外,还有有哪些Type呢?这就是我之前一直没有看懂的那些反射api了内容了。
    泛型的英文是GenericType 方法的泛型参数是method.getGenericParameterTpe 属性的泛型是 field.getGenericType 这二者都返回Type,不同的仅仅是方法返回的所有参数的Type[],而属性仅仅返回一个Type。此时的type不仅带有Class信息还带有<>里的数据。但是jdk却并没有为Type规定任何方法,唯一的办法是检查Type究竟是哪种子Type种实例,直到找到Class类型的实例为止。所以,要获得泛型的实际类型,要么是递归调用方法,要么是用while循环。
    区分泛型声明,使用泛型时传递类型参数,以及引用泛型这三者所指是很有意义的。这三者的区别就相当于是  方法声明的形式参数,调用方法传递实质参数,以及在方法体中用参数名使用某个参数的区别。以上例的GenericClass来说,类名后的T是泛型声明,属性使用的T是泛型引用。而new GenericClAass<Contract> 则时给泛型传递类型参数。GenericClassn内还有List<Number> 这是对给List接口声明的泛型传递类型参数,换言之用在class泛型声明<>里内的内容并不都是泛型声明,泛型声明可以组合其他类型描述自己。
    理解了以上的区别,再去看java反射包里那些各种type接口,就省事多了。
    还有一点是 泛型中 <>内的 extends包括了implements


    一,TypeVariable Type的子接口 实现类是TypeVariableImpl,这个是运行时用来处理泛型声明的。通过 type instenceof TypeVariable 返回true 然后 type = (TypeVariable)type 便可访问该接口提供的三个方法.也可以通过type.getClass() == TypeVariableImpl.class来判断,但是不推荐,因为实现类可能更新。 
    1,getName():String 返回形式泛型参数的名字 就是T E K 等。
    2,Type[] getBounds() 返回泛型声明的上边界类型。如果没有指定 T extends什么,那返回的Type 就是 Object.class。注意1,该方法并不一定返回Class实例,因为泛型的使用可以很复杂,一个泛型声明extedns的对象完全可能又是一个泛型。2,该方法虽然返回的是Type数组,但是经过笔者测试,该方法总是返回一个元素。笔者猜想这或许是考虑到可以返回空数组因此避免返回null(ps:不确定,待高人回答)。
    3,只有类(Class) 构造方法(Constractor),方法(Method)才能声明泛型,这三者都是GenericDeclaration的实现类。因此TypeVariable<D extends GenericDeclaration>只能接收GenericDeclarationd的类型参数。 D getGenericDeclaration() 返回声明泛型的对象 即某个Class,Constractor或Method