Generic的四个要点 #
- 虚拟机的角度来看并不存在 generic,只有普通的类的方法。 There are no generics in the virtual machines, only ordinary classes and methods.
- 所有 Type 参数都会用 Bounds 来替换。 All type parameters are replaced by their bounds.
- 为了维护多态机制,编译器会自动添加 Bridge 方法。 Bridge methods are synthesized to preserve polymorphism.
- 强制类型转换会由编译器自动插入。 Casts are inserted as necessary to preserve type safety.
根据 Generic Type 生成对应的 Raw Type 使用 Generic 的限制
Wildcard Type #
ArrayList有addAll方法,可以把ArrayList<Manager>所有元素加到ArrayList<Employee>中,但反过来却并不应该成立。为了解决这样的问题,Java语言的设计者发明了Wildcard Type来解决这类问题。 the generic class acts as a factory for ordinary classes. wildcards with supertype bounds let you write to a generic object, wildcards with subtype bounds let you read from a generic objects.
Subtype Bounds of Wildcards #
下面的例子中,accessor method 是安全的,而 mutator method 是不安全的。
Pair<Manager> managerBuddies = new Pair<Manager>(ceo, cfo);
Pair<? extends Employee> wildcardBuddies = managerBuddies; // OK
wildcardBuddies.setFirst(lowlyEmployee); // compile-time error
Pair<? extends Employee>的两个方法看起来会是下面这样:
? extends Employee getFirst()
void setFirst(? extends Employee)
getFirst 方法是安全的,它的返回值可以转换成指向 Employee 对象的引用。setFirst 方法却会导致编译错误,因为编译器只知道参数是 Employee 的子类型,却无法知道具体是什么类型。编译器拒绝将确定类型的参数传递给 setFirst 方法,因为 ? 可能会与传递进来的类型不匹配。(这里需要注意Java是强类型语言,编译器在参数传递之前必须保证类型的安全。)
Supertype Bounds for Wildcards #
Pair<? super Manager>的两个方法看起来会是下面这样:
void setFirst(? super Manager)
? super Manager getFirst()
setFirst 方法的参数是 Manager 的父类型,这一点编译器是可以预先知道的,能够传递给 setFirst 的参数的类型只能是Manager,Employee,Object三者之一,因此,setFirst 方法可以安全地得到调用。反过来,getFirst 方法却无法知道返回的会是什么类型,getFirst 方法在调用时会导致编译错误。
另外一种使用 Supertype Bounds for Wildcard 的例子:
public static <T extends Comparable<T>> T min(T[] a)
min 方法利用 Comparable 接口实现求最小值。String 继承了 Comparable<String>,min 方法能够成功地应用于 String。然而在碰到 GregorianCalendar 时却会出问题。GregorianCalendar -> Calendar -> Comparable<Calendar> 。由于没有 Comparable<GregorianCalendar> ,min 方法无法正确调用。解决的办法是:
public static <T extends Comparable<? super T>> T min(T[] a) . . .
Unbounded Wildcards #
使用 Unbounded Wildcard 的 Pair<?> 与 Raw Type 有很大不同。Pair<?> 的两个方法看起来会是下面的样子:
? getFirst()
void setFirst(?)
getFirst 方法返回的是 Object 对象,而 setFirst 方法永远无法调用,因为对指定类型编译器无法判断是否符合 setFirst 对类型的要求。 Unbounded Wildcard 一般只用在特别简单的场合,并且不会使用 mutator method。
Wildcard Capture #
假设要交换 Pair<?> 中的两个元素:
public static void swap(Pair<?> p)
由于这里使用了 Wildcard,因此无法知道两个元素的类型,写成下面的样子是错误的:
? t = p.getFirst(); // ERROR
解决的办法是自定义 helper method:
public static <T> void swapHelper(Pair<T> p)
{
T t = p.getFirst();
p.setFirst(p.getSecond());
p.setSecond(t);
}
原来的 swap 方法就可以通过调用 swapHelper 方法来实现:
public static void swap(Pair<?> p) { swapHelper(p); }
swapHelper 中的 T 担当了捕获 Wildcard 的类型的作用。
Generic 类型的继承规则 #
- Parameterized Type 总是能强制转换成 Raw Type。Parameterized Type 是 Raw Type 的 subtype。
- Generic Class 可以继承或实现其他 Generic Class。

- 使用了 Subtype Bounds for Wildcard 后的继承体系参看下图的例子。

- 使用了 Supertype Bounds for Wildcard 后的继承体系参看下图的例子。

反射中的 Generic #
Generic 的出现让反射 API 中添加了 Type 类,其继承体系图如下:

| Type | Example |
|---|---|
| TypeVariable | T extends Comparable<? super T> |
| WildcardType | ? super T |
| ParameterizedType | Comparable<? super T> |
| GenericArrayType | T[] |
带类型参数的类型在转换时的警告“Type Safety: Unchecked cast from …“处理 #
可以通过
@SuppressWarnings("unchecked")
限制编辑器警告,然而在运行时如果类型的转换出错,Java会抛出ClassCastException。也可以在转换前使用instanceof检测。 http://stackoverflow.com/questions/509076/how-do-i-address-unchecked-cast-warnings
Contents #
普通类中的Generic方法 Bounds for Type Variables