以下代码无法在Eclipse中编译。它说"类型Abc中的方法putHelper(List,int,E)不适用于参数(List< .capture#8-扩展E>",int,E)&# 34;
private <E> void putHelper(List<E> list, int i, E value) {
list.set(i, value);
}
public <E> void put(List<? extends E> list, int toPos, E value) {
// list.set(toPos,value);
putHelper(list, toPos, value);
}
我不明白为什么会这样? 因为下面的代码工作正常。
public <E> void put(List<? extends E> list,int fromPos, int toPos) {
putHelper(list,fromPos,toPos);
}
private <E> void putHelper(List<E> list,int i, int j) {
list.set(j,list.get(i));
}
我知道这里的helper方法能够捕获通配符类型,但为什么不在早期的代码中呢?
编辑:在第三种情况下,如果我将put方法中的类型参数更改为List&lt;。?超级E&gt;当我尝试从另一个采用列表的方法调用put()方法时,Eclipse并没有编译它。它说,&#34;方法put(List&lt;。?super E&gt;,int,E)在类型Abc中不适用于参数(List&lt; .capture#6-扩展E&gt;&#34; ,INT,E)&#34;
public static <E> void insertAndProcess(List<? extends E> list) {
// Iterate through the list for some range of values i to j
E value = list.get(i);
//Process the element and put it back at some index
put(list, i+1, value);
//Repeat the same for few more elements
}
private static <E> void putHelper(List<E> list, int i, E value) {
list.set(i, value);
}
public static <E> void put(List<? super E> list, int toPos, E value) {
putHelper(list, toPos, value);
}
这里,insertAndProcess()如何调用put()方法并在其实现中使用它,而用户仍然可以使用say ArrayList&lt; .Integer&gt;来调用这两个方法?
答案 0 :(得分:2)
这是因为获取和放置原则也被称为Producer Extends,Consumer Super的首字母缩写 PECS 。
在此SO问题中对此进行了解释:What is PECS (Producer Extends Consumer Super)?
但基本上是:
public <E> void put(List<? extends E> list, int toPos, E value) {
// list.set(toPos,value);
putHelper(list, toPos, value);
}
此处无法使用 <? extends E>
,因为List
被用作消费者(它正在使用元素),所以它应该使用super
而不是extends
。
public <E> void put(List<? super E> list, int toPos, E value) {
// list.set(toPos,value);
putHelper(list, toPos, value);
}
修改强>
在第二种情况下,List
充当制作者,因为它通过调用get()
来生成元素,因此您可以使用extends
。< / p>
然而,在你的第三个例子中,你们都得到并放入相同的列表,所以我认为你根本不能使用通配符。
编译:
public static <E> void insertAndProcess(List<E> list) {
// Iterate through the list for some range of values i to j
E value = list.get(i);
// Process the element and put it back at some index
putHelper(list, i+1, value);
// Repeat the same for few more elements
}
请注意,因为我们不需要使用任何通配符,因为我们从同一个列表中获取和设置,因此类型E
必须是相同的,无论它是什么。
答案 1 :(得分:1)
泛型有时候有点困难。尝试逐步分析它。
想象一下 - 在你的第一个例子中(不编译) - 你调用put
方法,类型变量E
替换为Number
。由于您使用list
约束输入参数List<? extends E>
,因此它可能是List<Integer>
。参数value
的类型为E
(请记住:E
为Number
),因此它可能是Double
。这不能被允许,因为您会尝试将Double
添加到List<Integer>
。
在第二个示例中,您只对输入参数list
有约束。没有其他参数依赖于E
。设置和获取列表将始终有效,因为有问题的元素必须属于同一类型。
解决方案:
始终提醒首字母缩略词 PECS 。见Wildcard (Java)。这意味着你应该将put
方法声明如下(在你的第一个例子中):
public <E> void put(List<? super E> list, int toPos, E value) {
putHelper(list, toPos, value);
}
答案 2 :(得分:0)
List<E>
将仅接受E类对象,其中List<? extends E>
可以接受E的任何子类。
当你说一个方法只能接受List<E>
传递List<? extends E>
时会混淆编译器,因为它不知道你的列表实际包含哪种类型的对象。
请注意,在java中,这是非法的:
List<E> list = new ArrayList<? extends E>();
E.g。 List<Number> list = new ArrayList<Integer>();
在第二个示例中,您的列表需要E的任何子类以及您传递的E对象列表,以便允许它。
例如。 List<? extends Number> list = new ArrayList<Integer>();