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
在网上看到过一个牛人在百度知道里对一个人的回答,他的意思大概是泛型只在编译的时候用于类型的检查,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
上一篇: ibatis动态标签sql
下一篇: 异步与非阻塞的区别