为什么没有定义Collections.unmodifiableList来返回List <! - ?扩展T - >而不是List <t>?

时间:2016-08-09 15:52:37

标签: java generics collections

Collections.unmodifiableList更改为返回List<? extends T>而不是List<T>会阻止在编译时添加和删除元素,而不是抛出运行时异常。

这种替代方案是否会导致严重问题,而这种问题会排除它?

1 个答案:

答案 0 :(得分:5)

Collections.unmodifiableList不返回List<? extends T>的根本原因是它是错误的返回类型。先来一点背景。目前的声明是

static <T> List<T> unmodifiableList(List<? extends T> list)

请注意,这需要List<? extends T>,但会返回List<T>。这符合Bloch的PECS原则(Naftalin / Wadler也称为 put and get principle )。由于返回的列表是不可修改的,因此您只能从中取出它,因此它是T类元素的“生产者”。

这为调用者提供了关于返回类型的更多灵活性。例如,人们可以这样做:

List<Double> src = Arrays.asList(1.0, 2.0, 3.0);
List<Number> list1 = Collections.unmodifiableList(src);

如果已有其他功能(例如,在List<Number>上运行),则此功能非常有用。

现在考虑一个替代声明:

static <T> List<? extends T> wildUnmodifiableList(List<? extends T> list)

这说明了一些不同的东西。这意味着它需要一个T的某个子类型的列表,并返回T的一些未知子类型的列表,其中可能与第一个类型不同。由于返回的列表是第一个列表的不可修改的视图,因此这没有任何意义。让我们用一个例子来说明:

List<Double> src = Arrays.asList(1.0, 2.0, 3.0);
List<? extends Number> list2 = wildUnmodifiableList(src);

这意味着我们传入List<Double>,但我们可能会返回其他类型的列表,可能是List<Integer>。再一次,这没有任何意义。

当您尝试使用此声明时,您可以看到此声明不正确。考虑Apache Commons Collections中的IterableUtils.frequency方法:

static <E,T extends E> int frequency(Iterable<E> iterable, T obj)

这适用于当前的声明:

List<Double> src = Arrays.asList(1.0, 2.0, 3.0);
List<Number> list1 = Collections.unmodifiableList(src);
int freq1 = frequency(list1, 0.0);

但是通配符版本失败了:

List<Double> src = Arrays.asList(1.0, 2.0, 3.0);
List<? extends Number> list2 = wildUnmodifiableList(src);
int freq2 = frequency(list2, 0.0); // COMPILE-TIME ERROR

原因是frequency的声明要求第二个参数是第一个参数的元素类型的子类型。在第一种情况下,它是Double,它是Number的子类型。但在第二种情况下,Double ? extends Number的子类型,导致类型不匹配。