铁文整理
12.9 反射和泛型
现在,Class类是泛型的。例如,String.class实际上是一个Class<String>类的对象(事实上是惟一的对象)。
类型参数十分有用,这是因为它允许Class<T>方法的返回类型更加具有针对性。下面Class<T>中的方法就使用了类型参数:
T newInstance()
T cast(Object obj)
T[] getEnumConstants()
Class<? super T> getSuperclass()
Constructor<T> getConstructor(Class... parameterTypes)
Constructor<T> getDeclaredConstructor(Class... parameterTypes)
newInstance方法返回一个实例,这个实例所属的类由默认的构造器获得。它的返回类型目前被声明为T,其类型与Class<T>描述的类相间,这样就免除了类型转换。
如果给定的类型确实是T的一个子类型,cast方法就会返回一个现在声明为类型T的对象,否则,抛出一个BadCastException异常。
如果这个类不是enum类或类型T的枚举值的数组,getEnumConstants方法将返回null。
最后,getConstructor与getDeclaredConstructor方法返回一个Constructor<T>对象。Constructor类也已经变成泛型,以便newInstance方法有一个正确的返回类型。
API:java.lang.Class<T> 1.0
-
T newInstance() 5.0:返回默认构造器构造的一个新实例。
-
T cast(Object obj) 5.0:如果obj为null或有可能转换成类型T,则返回obj;否则抛出BadCastException异常。
-
T[] getEnumConstants() 5.0:如果T是枚举类型,则返回所有值组成的数组,否则返回null。
-
Class<? super T> getSuperclass() 5.0:返回这个类的超类,如果T不是一个类或Object类,则返回null。
-
Constructor<T> getConstructor(Class... parameterTypes) 5.0
-
Constructor<T> getDeclaredConstructor(Class... parameterTypes) 5.0:获得公有的构造器,或带有给定参数类型的构造器。
API:java.lang.reflect.Constructor<T> 1.1
-
T newInstanca(Object... parameterTypes) 5.0:返回用指定参数构造的新实例。
12.9.1 使用Class<T>参数进行类型匹配
有时,匹配泛型方法中的Class<T>参数的类型变量很有实用价值。下面是一个具有一定权威的示例:
public static <T> Pair<T> makePair(Class<T> c)
throws InstantiationException, IllegalAccessException {
return new Pair<T>(c.newInstance(), c.newInstance());
}
如果调用makePair(Employee.class),Employee.class是类型Class<Employee>的一个对象。makePair方法的类型参数T同Employee匹配,并且编译器可以推断出这个方法将返回一个Pair<Employee>。
12.9.2 虚拟机中的泛型类型信息
Java泛型的卓越特性之一是在虚拟机中泛型类型的擦除。令人感到奇怪的是,擦除的类仍然保留一些泛型祖先的微弱记忆。例如,原始的Pair类知道源于泛型类Pair<T>,即使一个Pair类型的对象无法区分是由Pair<String>构造的还是由Pair<Employee>构造的。
类似地,看一下方法
public static Comparable min(Comparable[] a)
这是一个泛型方法的擦除
public static <T extends Comparable<? super T>> min(T[] a)
可以使用Java SE 5.0增加的反射API来确定:
-
这个泛型方法有一个叫做T的类型参数。
-
这个类型参数有一个子类型限定,其自身又是一个泛型类型。
-
这个限定类型有一个通配符参数。
-
这个通配符参数有一个超类型限定。
-
这个泛型方法有一个泛型数组参数。
换句话说,需要重新构造实现者声明的泛型类以及方法中的所有内容。但是,不会知道对于特定的对象或方法调用,如何解释类型参数。
注释:包含在类文件中,让泛型反射可用的类型信息与旧的虚拟机不兼容。
为了表达泛型类型声明,Java SE 5.0在java.lang.reflect包中提供了一个新的接口Type。这个接口包含下列子类型:
-
Class类,描述具体类型。
-
TypeVariable接口,描述类型变量(如T extends Comparable<? super T>)。
-
WildcardType接口,描述通配符(如? super T)。
-
ParameterizedType接口,描述泛型类或接口类型(如Comparable<? super T>)。
-
GenericArrayType接口,描述泛型数组(如T[] p)。
图12-5给出了继承层次。注意,最后4个子类型是接口,虚拟机将实例化实现这些接口的适当的类。
例12-4使用泛型反射API打印出给定类的有关内容。如果用Pair类运行,将会得到下列报告:
class Pair<T> extends java.lang.Object
public T getFirst()
public T getSecond()
public void setFirst(T)
public void setSecond(T)
如果使用PairTest2目录下的ArrayAlg运行,将会得到下列报告
public static <T extends java.lang.Comparable> Pair<T> minmax(T[])
本节末尾的API注释描述了示例程序中使用的这些方法。
例12-4 GenericReflectionTest.java
import java.lang.reflect.*;
import java.util.*;
/**
* @version 1.10 2007-05-15
* @author Cay Horstmann
*/
public class GenericReflectionTest {
public static void main(String[] args) {
// read class name from command line args or user input
String name;
if (args.length > 0)
name = args[0];
else {
Scanner in = new Scanner(System.in);
System.out.println("Enter class name (e.g. java.util.Collections): ");
name = in.next();
}
try {
// print generic info for class and public methods
Class<?> cl = Class.forName(name);
printClass(cl);
for (Method m : cl.getDeclaredMethods())
printMethod(m);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void printClass(Class<?> cl) {
System.out.print(cl);
printTypes(cl.getTypeParameters(), "<", ", ", ">", true);
Type sc = cl.getGenericSuperclass();
if (sc != null) {
System.out.print(" extends ");
printType(sc, false);
}
printTypes(cl.getGenericInterfaces(), " implements ", ", ", "", false);
System.out.println();
}
public static void printMethod(Method m) {
String name = m.getName();
System.out.print(Modifier.toString(m.getModifiers()));
System.out.print(" ");
printTypes(m.getTypeParameters(), "<", ", ", "> ", true);
printType(m.getGenericReturnType(), false);
System.out.print(" ");
System.out.print(name);
System.out.print("(");
printTypes(m.getGenericParameterTypes(), "", ", ", "", false);
System.out.println(")");
}
public static void printTypes(Type[] types, String pre, String sep,
String suf, boolean isDefinition) {
if (pre.equals(" extends ") && Arrays.equals(types, new Type[] { Object.class }))
return;
if (types.length > 0)
System.out.print(pre);
for (int i = 0; i < types.length; i++) {
if (i > 0)
System.out.print(sep);
printType(types[i], isDefinition);
}
if (types.length > 0)
System.out.print(suf);
}
public static void printType(Type type, boolean isDefinition) {
if (type instanceof Class) {
Class<?> t = (Class<?>) type;
System.out.print(t.getName());
} else if (type instanceof TypeVariable) {
TypeVariable<?> t = (TypeVariable<?>) type;
System.out.print(t.getName());
if (isDefinition)
printTypes(t.getBounds(), " extends ", " & ", "", false);
} else if (type instanceof WildcardType) {
WildcardType t = (WildcardType) type;
System.out.print("?");
printTypes(t.getUpperBounds(), " extends ", " & ", "", false);
printTypes(t.getLowerBounds(), " super ", " & ", "", false);
} else if (type instanceof ParameterizedType) {
ParameterizedType t = (ParameterizedType) type;
Type owner = t.getOwnerType();
if (owner != null) {
printType(owner, false);
System.out.print(".");
}
printType(t.getRawType(), false);
printTypes(t.getActualTypeArguments(), "<", ", ", ">", false);
} else if (type instanceof GenericArrayType) {
GenericArrayType t = (GenericArrayType) type;
System.out.print("");
printType(t.getGenericComponentType(), isDefinition);
System.out.print("[]");
}
}
}
API:java.lang.Class<T> 1.0
-
TypeVariable[] getTypeParameters() 5.0:如果这个类型被声明为泛型类型,则获得泛型类型变量,否则获得一个长度为0的数组。
-
Type getGenericSuperclass() 5.0:获得被声明为这一类型的超类的泛型类型,如果这个类型是Object或不是一个类类型,则返回null。
-
Type[] getGenericInterfaces() 5.0:获得被声明为这个类型的接口的泛型类型(以声明的次序),否则,如果这个类型没有实现接口,返回长度为0的数组。
API:java.lang.reflect.Method 1.1
-
TypeVariable[] getTypeParameters() 5.0:如果这个方法被声明为泛型方法,则获得泛型类型变量,否则返回长度为0的数组。
-
Type getGenericReturnType() 5.0:获得这个方法被声明的泛型返回类型。
-
Type[] getGenericParameterTypes() 5.0:获得这个方法被声明的泛型参数类型。如果这个方法没有参数,返回长度为0的数组。
API:java.lang.reflect.TypeVariable 5.0
-
String getName():获得类型变量的名字。
-
Type[] getBounds():获得类型变量的子类限定,否则,如果该变量无限定,则返回长度为0的数组。
API:java.lang.reflect.WildcardType 5.0
-
Type[] getLowerBounds():获得这个类型变量的子类(extends)限定,否则,如果没有子类限定,则返回长度为0的数组。
-
Type[] getUpperBounds():获得这个类型变量的超类(super)限定,否则,如果没有超类限定,则返回长度为0的数组。
API:java.lang.reflect.ParameterType 5.0
-
Type getRawType():获得这个参数化类型的原始类型。
-
Type[] getActualTypeArguments():获得这个参数化类型声明时所使用的类型参数。
-
Type getOwnerType():如果是内部类型,则返回其外部类型,如果是一个顶级类型,则返回null。
API:java.lang.reflect.GenericArrayType 5.0
-
Type getGenericComponentType():获得声明该数组类型的泛型组合类型。
现在已经学习了如何使用泛型类以及在必要时如何自定义泛型类和泛型方法。同样重要的是,学习了如何解译在API文档和错误消息中遇到的泛型类型声明。要想了解有关Java泛型更加详尽的信息,可以到http://angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html上求助。那里有一个很好的常见问题解答列表(也有一些不太常见的)。
在下一章中,将学习Java集合框架如何使用泛型。