TIP24消除非受检警告
尽可能的消除每一个unchecked警告。
如果无法消除警告,同时可以证明引起警告的代码是类型安全的,才可以用注解@SuperessWarnings(“unchecked”)注解来禁止这条警告。每当使用这个注解时,一定要写注释说明为什么此处的代码是安全的。
TIP25列表优先于数组
先看下面的代码片段:
Object[] objectArray = new Long[1];
objectArray[0] = "not fit";
List<Object> ol = new LinkedList<Long>(); //此处编译不通过
ol.add("not fit");
数组是协变的(covariant)。相反,泛型是不可变的(invariant)。
如果Sub是Super的子类型,那么Sub[]
也是Super[]
的子类型。
而List<Sub>
不是List<Super>
的子类型。
数组是具体化的(reified)
因此,数组会在运行时才会知道并检查元素类型约束。如果将String保存到Long数组中,将会在运行时抛出一个ArrayStoreException的异常。
相比之下,泛型则通过擦除来实现元素类型约束的。泛型在编译时会强化元素类型信息,运行时会擦除元素类型信息。
基于上面的原因,数组与泛型不能很好的混合使用。比如,无法实例化这样的数组: E[] es = new E[1]
。同样的, 类似List<String>[]
这样的也无法实例化。它们是不可具体化的。
如果混合使用时,收到了编译时错误或警告,就应当考虑用列表代替数组。
TIP26优先考虑泛型
先来看看一个简单栈的实现
public class DouStack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public DouStack() {
elements = new Object[DEFAULT_INITIAL_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);
}
}
}
OK,我们尝试把它泛型化
public class DStack<E> {
private E[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public DStack() {
elements = new E[DEFAULT_INITIAL_CAPACITY];
}
public void push(E e) {
ensureCapacity();
elements[size++] = e;
}
public Object 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);
}
}
}
ok,构造器中的elements = new E[DEFAULT_INITIAL_CAPACITY];
编译器会报错了。TIP25中也说明了,无法实例化不可具体化的类型。
编写此类用数组支持的泛型时,都可能会遇到此类问题。解决方案有两种:
创建一个Object数组,并转换成泛型数组。这种用法是合法的,但不是类型安全的,并会收到编译器的未受检的警告。参考24和25条TIPS。
将elements的类型从
E[]
改为Object[]
,将会得到一个编译错误:然后这样:
E result = (E) elements[--size];
这样就没有错误,但多了一条警告:Type safety: Unchecked cast from Object to E因为E是不可具体化类型,所以编译器无法在运行时检验转换。如果自己能保证这个转换是安全的,可以加上注解:
`@SuppressWarnings("unchecked") E result = (E) elements[--size];`
请注意这条注解的位置,是用于声明result的。在使用这条注解时,应该放在最小的需求范围内。
由于禁止数组类型的未受检转换比禁止标量类型(scalar type)更加危险,建议采用第二种方案。