Item 26告诉我们要优先考虑泛型。
原因
比如下面的例子里,在参数化collection的时候:
// object-based collection - a prime candidate for fenerics
public class Stack {
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++];
}
public Object pop() {
if (size == 0) {
throw new EmptyStackException();
}
Object result = elements[--size];
elements[size] = null; // eliminate obsolete reference
return result;
}
public boolean isEmpty() {
return size == 0;
}
private void ensureCapacity() {
if (elements.length == size) {
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
}
在上面的例子中,我们用的是Object,所以在具体使用Stack里面的元素的时候,我们需要进行类型转换(cast)。而这些类型转换在run-time是很容易失败的。比起run-time的failure,我们更希望在compiler的时候就抓住。
问题
现在我们把上面的Object都用泛型来表示:
// initial attempt to generify Stack - won't compile
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++];
}
public E pop() {
if (size == 0) {
throw new EmptyStackException();
}
E result = elements[--size];
elements[size] = null; // eliminate obsolete reference
return result;
}
... // no change in isEmpty or ensureCapacity
}
结果我们会遇到下面的error/warning:
Stack.java:8: generic array creation
elements = new E[DEFAULT_INITIAL_CAPACITY];
这是因为根据Item 25里面所说,我们不能创建一个泛型数组。
解决方法
我们有两个解决方法:
- 避开这个限制。我们创建Object array,然后再cast成泛型。
- 把stack里的field E[] elements变成Object[] elements。
第一个方法中,当我们创建Object array,然后再cast成泛型时,compiler会给我们一个warning:
Stack.java:8: warning: [unchecked] unchecked cast
found : Object[], required: E[]
elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
这个warning的目的是为了typesafe。因为我们知道Stack里面所有加进去的元素都是泛型E,这个cast是typesafe的,所以我们可以抑制这个warning。
// the elements array will contain only E instances from push(E).
// this is sufficient to ensure type safety, but the runtime
// type of the array won't be E[]; it will always be Object[]!
@SuppressWarnings("unchecked")
public Stack() {
elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
}
第二个方法中,我们把stack里的field E[] elements变成Object[] elements。这样会有下面的error:
Stack.java:19: incompatible types
found : Object, required: E
E result = elements[--size];
如果我们对应的把Object cast成E,error没有了,但会有下面的warning:
Stack.java:19: warning: [unchecked] unchecked cast
found : Object, required: E
E result = (E) elements[--size];
同理,因为我们知道Stack里面所有加进去的元素都是泛型E,这个cast是typesafe的,所以我们可以抑制这个warning。
// appropriate suppression of unchecked warning
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;
}
这两种方法都可以使用。
Reference
网友评论