学习范型的第一课就被警告说,范型信息再编译之后是拿不到的,因为已经被擦除掉了。如果不深入研究,这个观点很容易给人以这样的错觉:只要代码里面用了范型的地方,编译之后,是没法拿到这部分信息的!
关于这点的错误之处,可以参考撒伽的这篇文章。 按他的解释是说:
位于声明一侧的,源码里写了什么到运行时就能看到什么;
位于使用一侧的,源码里写什么到运行时都没了。
这里,最根本的原因,还是因为jdk1.5之后,将范型类、方法、属性的信息,都固化到了编译之后的class文件中(具体的例子可以参考上面提高文章中反编译class文件之后拿到的信息),java本身也就有能力通过反射的API来获取这部分信息了。关于这个使用方法的实例,《Generic Data Access Objects》提供了演示。
不过,光看上面的文章,在自己操作处理范型信息时,java提供的API还是有点问题的。
这里,我们必须先搞清楚下面提到的几个概念:
- type parameters:范型类或者范型接口声明里面的“范型参数”,比如:List<E>这个范型接口中,有一个type parameter,就是E。
- parameterized types: “泛化类型”是指范型类或这范型接口本身,比如:List<String>就是一个具体的parameterized type。
- actual type parameter:范型使用中,实际使用的真实类型。比如:List<String>中,String就是上面提到的E的actual type parameter。
- raw type:踢掉“范型参数”之后,剩下的“裸类型”。比如:List<E>中,List就是List<E>的raw type。
了解了上面的几个基本概念之后,我们分别来看类(或者接口)、方法、属性对应的范型信息获取的API。
一、类(或者接口):参考下面是Class中getTypeParameters方法的实现。
public TypeVariable<Class<T>>[] getTypeParameters() {
if (getGenericSignature() != null)
return (TypeVariable<Class<T>>[])getGenericInfo().getTypeParameters();
else
return (TypeVariable<Class<T>>[])new TypeVariable[0];
}
根据上面提到的概念,Class中这个方法本身的语义不难理解。
这里,返回了一个TypeVariable的数组。关于,TypeVariable接口,只有3个方法:String getName()、D getGenericDeclaration()、Type[] getBounds()。关于这三个方法的使用,具体来看一下下面的例子:
public class GenericT<T,E extends GenericT.B & Serializable> {
private T innerT;
private Map<String,T> nameMap;
//just for demo
public static class A{
}
//just for demo
public static class B extends A{
}
}
public class Main {
public static void main(String[] args) {
showGenericTypeInfo();
}
public static void showGenericTypeInfo(){
TypeVariable<Class<GenericT>>[] typeVariable = GenericT.class.getTypeParameters();
for (TypeVariable<Class<GenericT>> classTypeVariable : typeVariable) {
System.out.println("TypeVariable.getName() : " + classTypeVariable.getName());
System.out.println("TypeVariable.getClass() : " + classTypeVariable.getClass());
StringBuilder sb = new StringBuilder( );
for(Type type : classTypeVariable.getBounds()){
sb.append( ((Class)type).getCanonicalName() ).append( "," );
}
System.out.println("TypeVariable.getBounds() : " + sb.substring(0,sb.length()-1));
System.out.println("TypeVariable.getGenericDeclaration() : " + classTypeVariable.getGenericDeclaration());
}
}
}
这里,为了演示TypeVariable.getBounds()方法,故意把GenericT的第二个type parameter继承了两个type(一个是他的内部类,一个是Serializable接口)。
运行上面的Main.main(),可以得到如下输出:
TypeVariable.getName() : T
TypeVariable.getClass() : class sun.reflect.generics.reflectiveObjects.TypeVariableImpl
TypeVariable.getBounds() : java.lang.Object
TypeVariable.getGenericDeclaration() : class com.sky.www.generic.GenericT
TypeVariable.getName() : E
TypeVariable.getClass() : class sun.reflect.generics.reflectiveObjects.TypeVariableImpl
TypeVariable.getBounds() : com.sky.www.generic.GenericT.B,java.io.Serializable
TypeVariable.getGenericDeclaration() : class com.sky.www.generic.GenericT
二、方法:(参考Method中提供的3个public的跟这个相关的方法)
public Type[] getGenericParameterTypes() {
if (getGenericSignature() != null)
return getGenericInfo().getParameterTypes();
else
return getParameterTypes();
}
public Type getGenericReturnType() {
if (getGenericSignature() != null) {
return getGenericInfo().getReturnType();
} else { return getReturnType();}
}
public Type[] getGenericExceptionTypes() {
Type[] result;
if (getGenericSignature() != null &&
((result = getGenericInfo().getExceptionTypes()).length > 0))
return result;
else
return getExceptionTypes();
}
这3个方法,分别对应获取方法的参数,返回值,异常的范型信息。注意,3个方法的返回值都是一个最上层的Type接口,实际使用时,就可能需要使用者自己去强转一下具体的子接口了。毕竟,Type只是个最高层抽象的类型接口,里面没有任何可以使用的方法,比较常用的子接口至少包括如下几个:
- TypeVariable (java.lang.reflect):上面类的范型信息中提到过。
- ParameterizedType (java.lang.reflect) :参考上面的概念,比如List<String>
- WildcardType (java.lang.reflect):使用范型通配符?类型,比如List<? extends String>
- GenericArrayType (java.lang.reflect) : 字面解释就是“范型数组类型”,主要代表的抽象实体是范型相关的数组。
上面四个最常用的子Type,前三个比较好理解,第四个GenericArrayType说起来有点拗口。(当然Type除了这四个子接口外,在常用的一个实现类就是Class本身了,这个不要忘记,下面的演示中也有示例)来看个下面的具体的例子,这个例子主要是以getGenericReturnType为基础展示的,其他两个完全类似:
public class GenericArrayTypeTest<T> {
// component type is a type variable
public T[] typeParameterDemo(){
return null;
}
// component type is a parameteriazed type
public List<String>[] listArrayDemo(){
return null;
}
// component type is a int.class
public int[] listInt(){
return null;
}
}
public class Main {
public static void main(String[] args) {
showGenericArrayType();
}
public static void showGenericArrayType(){
for(Method method :GenericArrayTypeTest.class.getDeclaredMethods()){
System.out.println("############################################");
System.out.println("Method name : "+method.getName());
Type type = method.getGenericReturnType();
if(type instanceof GenericArrayType){
Type componentType = ((GenericArrayType)type).getGenericComponentType();
System.out.println("GenericArrayType's componentType is : "+componentType);
if(componentType instanceof TypeVariable){
System.out.println(componentType+" is a TypeVariable!");
TypeVariable typeVariable = (TypeVariable)componentType;
System.out.println("TypeVariable.getGenericDeclaration() is : "+typeVariable.getGenericDeclaration());
}else if(componentType instanceof ParameterizedType){
System.out.println(componentType+" is a ParameterizedType!");
ParameterizedType parameterizedType = (ParameterizedType)componentType;
System.out.println("It's raw type is : "+parameterizedType.getRawType());
System.out.println("It's owner type is : "+parameterizedType.getOwnerType());
for(Type type1 : parameterizedType.getActualTypeArguments()){
System.out.println("Actual Type is : "+type1);
}
}
}else if(type instanceof Class){
System.out.println("GenericeReturnType is a class : "+type);
Class cl = (Class)type;
System.out.println("It's name is : " + cl.getCanonicalName());
if(cl.isArray()) {
System.out.println("It is a array!");
Class<?> componentType = cl.getComponentType();
System.out.println("The component type of this array is : "+componentType);
}
}
System.out.println("############################################");
}
}
}
上面一段程序的输出如下:
############################################
Method name : typeParameterDemo
GenericArrayType's componentType is : T
T is a TypeVariable!
TypeVariable.getGenericDeclaration() is : class com.sky.www.generic.GenericArrayTypeTest
############################################
############################################
Method name : listArrayDemo
GenericArrayType's componentType is : java.util.List<java.lang.String>
java.util.List<java.lang.String> is a ParameterizedType!
It's raw type is : interface java.util.List
It's owner type is : null
Actual Type is : class java.lang.String
############################################
############################################
Method name : listInt
GenericeReturnType is a class : class [I
It's name is : int[]
It is a array!
The component type of this array is : int
############################################
三、属性(Field里面只提供了一个public出来的方法,用于获得域属性的范型信息)
public Type getGenericType() {
if (getGenericSignature() != null)
return getGenericInfo().getGenericType();
else
return getType();
}
关于这个API的使用,这里就不举例了,因为获得还是最上层的Type信息,所以具体使用时,还是跟上面Method的演示的差不错,通常涉及到转换成具体的子Type接口才能获得更为具体的信息。具体操作层面上,就是根据情况转换成上面的四个子接口之一或者Class来操作了。
分享到:
相关推荐
Java 范型Java 范型
虽然Java范型在编译时提供了类型安全,但在运行时,所有范型信息都会被擦除,只保留原始类型的信息。这是因为Java虚拟机(JVM)并不支持泛型,为了与现有的JVM兼容,Java采用了类型擦除(Type Erasure)的技术。这...
- **类型擦除**:Java泛型在编译后会进行类型擦除,实际生成的字节码中不会包含类型参数。这意味着在运行时,泛型对象实际上是无类型的,但编译时的类型检查仍然有效。 3. **泛型通配符** - **无界通配符**:`?`...
此外,Java泛型还有擦除的概念,即在编译后,泛型信息会被消除,转而使用原始类型。这是因为Java的泛型是类型安全的,但不是运行时类型检查的。尽管如此,泛型仍然在编译时提供了类型检查,使得我们能够在开发阶段就...
Java泛型是Java 5版本引入的一个重要特性,极大地增强了代码的类型安全性和可读性。泛型允许我们在编写代码时指定容器(如List、Set、Map等集合类)能够存储的数据类型,从而避免了不必要的类型转换,并在编译时期就...
在泛型类中,虽然类型参数在编译时被擦除,但仍然可以通过一些方式获取类型信息。 泛型类的基本结构如下: ```java public class MyClass<T> { private T myVariable; public MyClass(T value) { this....
泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。 Java语言引入泛型的好处是...
在Java中,范型也被称为参数化类型。例如,`HashMap, Value>`就是一个范型类,其中`Key`和`Value`是类型参数,代表了存储的数据的键和值的类型。在创建`HashMap`实例时,我们可以指定具体的类型,如`HashMap, ...
Java 泛型是一种强大的语言特性,它在J2SE 5.0中引入,极大地提升了代码的类型安全性和效率。泛型允许我们在编写类、接口和方法时指定一种或多种类型参数,使得代码能够处理多种数据类型,同时避免了运行时的类型...
在本文中,我们将深入探讨如何使用Java的Socket编程来实现一个简单的基于P2P(Peer-to-Peer)范型的即时聊天系统。P2P网络架构允许每个节点既是客户端也是服务器,这种模式使得数据传输更加分散,提高了系统的可扩展...
范型参考 (1).java
范型参考 (2).java
- Java范型在编译后会进行类型擦除,实际运行时并不保留类型参数信息,因此在运行时无法通过反射获取泛型类型。 - 擦除后,泛型类的实例将退化为无参数类型,但编译器会检查类型安全。 4. 泛型与多态 - 泛型与...
《C++多范型设计》是一本深入探讨C++编程语言中模板技术的专著,由知名软件工程师James O. Coplien撰写,并由鄢爱兰、周辉等翻译成中文版,ISBN号为9787508318240。这本书的核心主题是C++中的泛型编程,它是C++编程...
本例子说明演示了Java范型使用的动机、范型类的使用、范型方法的使用,以及范型的缺陷:类型擦除(type erasure).因为,现在Java的反射机制不能确定集合中的对象类型! 在实际应用中,如果能够灵活应用范型和反射...
Java 实现泛型List的源码,基本实现了List接口的全部所有方法。欢迎大家发表自己的观点和建议。
消息队列机制可以有效地管理异步消息传递,确保消息按照发送顺序被处理。锁机制则用于在处理关键数据时防止数据冲突,确保数据的完整性和一致性。 在MDM系统中,当某个分系统的数据发生变化时,系统会生成一个包含...
《范型程序设计与 STL》是一本深入探讨C++编程中的关键概念和技术的书籍,主要聚焦于范型(Generic Programming)和标准模板库(Standard Template Library,简称STL)。范型编程是一种强大的软件开发方法,它允许...
### JDK 1.5 中的范型 #### 引言 自 JDK 1.5 开始,Java 语言正式引入了一项重要的特性——泛型(Generics),这使得 Java 成为了一个更为强大且灵活的语言。泛型允许开发人员编写出类型安全的通用类或方法,从而...