私有帮助器方法,用于捕获通用方法的通配符类型

时间:2014-06-24 05:57:43

标签: java generics compiler-errors bounded-wildcard helpermethods

以下代码无法在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;来调用这两个方法?

3 个答案:

答案 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(请记住:ENumber),因此它可能是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>();