将Collections.unmodifiableList
更改为返回List<? extends T>
而不是List<T>
会阻止在编译时添加和删除元素,而不是抛出运行时异常。
这种替代方案是否会导致严重问题,而这种问题会排除它?
答案 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
的子类型,导致类型不匹配。