列表优先于数组

列表优先于数组

不同点

  1. 数组和泛型的不同首先体现在数组是covariant的,所以如果Sub是Super的一个子类型,那么数组类型Sub[]也是数组类型Super[]的子类型。相反,泛型列表对此则有限制。这意味着:
1
2
Object[] objectArray = new Long[1];
objectArray[0] = "I don't fit in"; //在运行时会报错

但如果使用列表:

1
2
List<Object> ol = new ArrayList<Long>(); // Incompatible types
ol.add("I don't fit in");
  1. 数组是具化的,数组只有在运行时才能直到并检查元素类型,而泛型是通过擦除来实现的,这意味着泛型只在编译时进行类型约束的检查,而运行时是忽略元素类型的。因此无法混合使用数组和泛型,以下的操作都是不合法的:
1
new List<E>[], new List<String>[], new E[]

优先使用列表

当你强转成数组类型时,若得到一个泛型数组创建错误或者未检查强转警告,最好的解决办法是,总是优先采用集合类型List<E>,而不是数组类型E[]。

考虑这样一个类,构造器接受一个集合:

1
2
3
4
5
6
7
8
9
10
public class Chooser {
private final Object[] choiceArray;
public Chooser(Collection choices) {
choiceArray = choices.toArray();
}
public Object choose() {
Random rnd = ThreadLocalRandom.current();
return choiceArray[rnd.nextInt(choiceArray.length)];
}
}

那么在使用时,我们每次都要在调用choose方法之后,将Object类型转换为需要的类型,有可能强转失败,如果我们使用泛型:

1
2
3
4
5
6
public class Chooser<T> {
private final T[] choiceArray;
public Chooser(Collection<T> choices) {
choiceArray = choices.toArray();
}// choose method unchanged
}

这样会编译报错,除非我们强制换位Object数组:

1
choiceArray = (T[]) choices.toArray();

这样只会产生一个警告,因为编译器无法保证运行时强转的安全性。当然,我们可以消除warning,但最佳的做法还是使用泛型列表:

1
2
3
4
5
6
7
8
9
10
public class Chooser<T> {
private final List<T> choiceList;
public Chooser(Collection<T> choices) {
choiceList = new ArrayList<>(choices);
}
public T choose() {
Random rnd = ThreadLocalRandom.current();
return choiceList.get(rnd.nextInt(choiceList.size()));
}
}