我理解,存在低限通配符的一个原因是,在添加新元素时,集合不是不可变的.
例如.
List<? extends Number> obj = new ArrayList<>();//Now this list is immutable
obj.add(new Integer(5));//Does not compile
List<? super Number> objTwo = new ArrayList<>();//This list is mutable
objTwo.add(new Integer(5));//Compiles
以下不编译因为我试图得到数字的长值.
Q1:我可以使用哪些方法?只有Objects方法?:
public void testLowerBounds(List<? super Number> numbers){
if (!numbers.isEmpty()){
System.out.println(numbers.get(0).longValue());//Does not compile
}
}
我的问题是如何产生的:
我目前正在学习流,本书指定了以下流方法:
Optional<T> min(Comparator<? super T> comparator)
并实现如下:
Stream<String> s = Stream.of("monkey", "ape", "bonobo");
Optional<String> min = s.min((s1, s2) -> s1.length()—s2.length());
Q2:比较器在使用时如何允许使用字符串方法?
如果我必须回答Q2:我会说可选是指定“你必须传递一个具有泛型类型”字符串“的比较器的实现或者实现”字符串“的东西.我会说这个是正确的吗?
期待你的回复.
最佳答案 首先,您不应将通配符类型参数与可变性混淆.在List的元素类型中使用通配符不会阻止修改,它只对您可以对列表执行的操作施加一些实际限制.
将列表声明为List<? extends Number>意味着引用的列表具有Number的实际元素类型或Number的子类,例如它可以是List< Integer>或列表< Double>.因此,您无法添加任意Number实例,因为您无法知道它是否与实际元素类型兼容.
但是您仍然可以添加null,因为已知null引用与所有引用类型兼容.此外,您始终可以从列表中删除元素,例如呼叫删除或清除没有问题.您还可以调用Collections.swap(list,index1,index2)之类的方法,这很有意思,因为有关通配符类型的正式规则调用list.set(index1,list.get(index2))是不合法的,但是将列表传递给另一个可能使用非通配符类型变量来表示列表元素类型的方法.这显然是正确的,因为它只设置源自同一列表的元素,这些元素必须兼容.
同样,如果你有一个Comparator< Number>,你可以调用Collections.sort(list,comparator)作为一个可以处理任意数字的比较器,能够处理列表中实际存储的任何数字.
总结一下,有吗?扩展集合的元素类型不会阻止修改.
如上所述,您不能将任意新元素插入到实际元素类型可能是绑定的未知子类的列表中,例如List<?扩展数字>.但是您可以保证在检索元素时获得Number实例,因为Number的每个子类型实例也是Number的实例.当您声明List<?时super Number>,其实际元素类型可能是Number或超类型Number,例如对象或可序列化.您可以插入任意数字实例,因为您知道它将与列表具有的任何实际元素类型兼容,因为它是超类型的数字.检索实例时,您只知道它是Object的实例,因为它是所有实例的超类型.要比较?扩展案例,有一个?超级声明不会阻止阅读,它只会带来一些实际限制.同样,你仍然可以将它传递给Collections.swap,因为无论我们对实际类型知之甚少,插入我们刚从同一列表中检索到的内容都可行.
在你的第二个问题中,你让双方感到困惑.你现在不是在看min的实现,而是在调用者处. min的声明(Comparator<?super T> c)允许调用者传递任何用T或超类型T参数化的比较器.因此当你有一个Stream< String>时,传递一个Comparator< String> ;到min方法,这正是你通过(s1,s2)实现的 – > s1.length() – s2.length()lambda表达式(但是,我更喜欢Comparator.comparingInt(String :: length)).
在min的实现中,确实不知道比较器的T或实际类型参数是什么.但是,知道任何类型为T的流元素都可以传递给比较器的比较方法,这可能需要T或超类型的T.