sub:Generic Programming in Java

sub:Generic Programming in Java

Generic的四个要点 #

  1. 虚拟机的角度来看并不存在 generic,只有普通的类的方法。 There are no generics in the virtual machines, only ordinary classes and methods.
  2. 所有 Type 参数都会用 Bounds 来替换。 All type parameters are replaced by their bounds.
  3. 为了维护多态机制,编译器会自动添加 Bridge 方法。 Bridge methods are synthesized to preserve polymorphism.
  4. 强制类型转换会由编译器自动插入。 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 类型的继承规则 #

  1. Parameterized Type 总是能强制转换成 Raw Type。Parameterized Type 是 Raw Type 的 subtype。
  2. Generic Class 可以继承或实现其他 Generic Class。
  3. 使用了 Subtype Bounds for Wildcard 后的继承体系参看下图的例子。
  4. 使用了 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