第26条:优先考虑泛型
考虑第6条的简单堆栈实现:
public class Stack {
pprivate Object[] elements;
private int size = 0;
private static final int DEFAULT_INITAL_CAPACITY = 16;
public Stack() {
elements = new Object[DEFAULT_INITAL_CAPACITY];
}
public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}
public Object pop() {
if(size == 0) {
throw new EmptyStackException();
}
Object result = elements[--size];
elements[size] = null;
return result;
}
public boolean isEmpty() {
return size == 0;
}
private void ensureCapacity() {
if(elements.length == size)
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
这个类是泛型化的主要备选对象,换句话说,可以适当的强化这个类来利用泛型。根据实际情况来看,必须转换从堆栈中弹出的对象,以及可能在运行时失败的那些转换。将类泛型化的第一个步骤,就是给他的声明添加一个或者多个类型参数。在这个例子中有一个类型参数,它表示堆栈的元素类型,这个参数的名称通常为E。
下一步是用相应的类型参数替换所有的Object类型,然后试着编译最终的程序:
public class Stack<E> {
pprivate E[] elements;
private int size = 0;
private static final int DEFAULT_INITAL_CAPACITY = 16;
public Stack() {
elements = new E[DEFAULT_INITAL_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;
return result;
}
public boolean isEmpty() {
return size == 0;
}
private void ensureCapacity() {
if(elements.length == size)
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
上面代码通常会出现一个错误或者警告,cannot create a generic array of E
解决方法:
1.直接绕开创建泛型数组的禁令,不创建泛型数组,创建一个Object数组,并将它转换成泛型数组类型,但是编译器仍然出现了一条警告。这种用法是合法的,但(整体上而言)不是类型安全的:
elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
2.将elements域的类型从E[]改为Object[]:
private Object[] elements;
E result = (E) elements[--size];
产生一条警告,因为可以保证类型安全,所以所以可以用SupressWarning注释忽略掉该警告。
具体选择哪种方法来处理泛型数组创建错误,则主要看个人的偏好了,所有其他的东西都一样,但是禁止数组类型的未受检转换比禁止标量类型的更加危险,所以建议采用第二种,但是在比Stack更实际的泛型类中,或许代码中会有多个地方需要从数组中读取元素,因此选择第二种方法需要多次转换成E,而不是只转换E[],,这也是第一种方法之所以更常用的原因。
总而言之,使用泛型比使用需要在客户端代码中进行类型转换的类型来的更加安全,也更加容易。在设计新类型的时候,更确保他们不需要这种类型转换就可以使用。这通常意味着要把类做成泛型的。这对于这些类型的新用户来说会变得更加轻松,又不会破坏现有的客户端。