一般来说,在自己的声明中进行参数化,以及使用JDK提供的泛型类型和方法,都不是特别困难。难得是自己写的泛型类型,但这是值得花时间学习的。
案例(来自第七条):
public class Stack {
// 基于Object的集合---选择对这个类进行泛型化
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack() {
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}
public E pop() {
if (size == 0)
throw new EmptyStackException();
E result = elements[--size];
elements[size] = null; // Eliminate obsolete reference
return result;
}
... // no changes in isEmpty or ensureCapacity
}
下一步就是将所有使用到Object类型的地方都替换为相应的类型参数,然后尝试编译到的程序:
public class Stack<E> {
private E[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack() {
elements = new E[DEFAULT_INITIAL_CAPACITY];
}
public void push(E e) {
ensureCapacity();
elements[size++] = e;
}
public E pop() {
if (size == 0)
throw new EmptyStackException();
E result = elements[--size];
elements[size] = null; // Eliminate obsolete reference
return result;
}
... // no changes in isEmpty or ensureCapacity
}
这时出现了编译错误,elements = new E[DEFAULT_INITIAL_CAPACITY];因为我们不可具体化的类型(如E)数组。有两种方法来解决:
第一种规避了对泛型数组创建的禁用:创建一个 Object
数组并将其转换为泛型数组类型
可以加上声明:
@SuppressWarnings("unchecked")
public Stack() {
elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
}
第二种是将属性元素的类型从 E[]
更改为 Object[]
,当然会有一个错误;
可以通过将从数组中检索到的元素转换为 E
来将此错误更改为警告:
E result = (E) elements[--size];
可以加上声明:
public E pop() {
if (size == 0)
throw new EmptyStackException();
// push requires elements to be of type E, so cast is correct
@SuppressWarnings("unchecked") E result =
(E) elements[--size];
elements[size] = null; // Eliminate obsolete reference
return result;
}
第一个更可读:数组被声明为 E[] 类型,清楚地表明它只包含 E 实例。 它也更简洁:在一个典型的泛型类中,你从代码中的许多点读取数组; 第一种技术只需要一次转换(创建数组的地方);
而第二种技术每次读取数组元素都需要单独转换。 因此,第一种技术是优选的并且在实践中更常用。 但是,它确实会造成堆污染(heap pollution)( 32 ):数组的运行时类型与编译时类型不匹配(除非 E 碰巧是 Object)。 这使得一些程序员非常不安,他们选择了第二种技术,尽管在这种情况下堆的污染是无害的。
总而言之,与需要在客户端代码中进行强制类型转换的类型相比,泛型类型更安全,而且更容易使用。当设计新的类型时,应该确保他们可以在不进行此类转换的情况下使用。这用通常意味着要将其设计为泛型类型。如果有任何一个现有的类型本应该是泛型类型,但实际上却不是,那就将其泛型化。这将使这些类型的新用户使用起来更容易,同时不会破坏已有的客户端。
所有文章无条件开放,顺手点个赞不为过吧!