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

use*_*408 1 java generics collections

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

是否存在由此替代方案引起的严重问题,从而排除它?

Stu*_*rks 5

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

static <T> List<T> unmodifiableList(List<? extends T> list)
Run Code Online (Sandbox Code Playgroud)

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

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

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

这在已经存在其他功能的情况下是有用的,例如,操作List<Number>.

现在考虑一个替代声明:

static <T> List<? extends T> wildUnmodifiableList(List<? extends T> list)
Run Code Online (Sandbox Code Playgroud)

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

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

这意味着我们传入了一个List<Double>但是我们可能会返回一些其他类型的列表List<Integer>.同样,这没有任何意义.

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

static <E,T extends E> int frequency(Iterable<E> iterable, T obj)
Run Code Online (Sandbox Code Playgroud)

这适用于当前的声明:

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

但它失败了通配符版本:

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
Run Code Online (Sandbox Code Playgroud)

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