【问题标题】:Private helper method to capture wildcard type for generic methods用于捕获泛型方法的通配符类型的私有辅助方法
【发布时间】:2014-06-24 05:57:42
【问题描述】:

以下代码无法在 Eclipse 中编译。它说“Abc 类型的 putHelper(List,int,E) 方法不适用于参数 (List <.capture extends e>",int,E)"

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));
  }

而且我知道这里的辅助方法能够捕获通配符类型,但为什么不能在早期的代码中?

编辑:在第三种情况下,如果我将 put 方法中的类型参数更改为 List<. super e> 并且当我尝试从另一个采用列表的方法调用 put() 方法时,Eclipse 不会编译它。它说,“Abc 类型的 put(List<. super e>,int,E) 方法不适用于参数 (List <.capture extends e>",int,E)"

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()方法并在其实现中使用它,而用户仍然可以通过说ArrayList<.integer>调用这两个方法?

【问题讨论】:

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


    【解决方案1】:

    这是因为 Get 和 Put 原则,也称为首字母缩略词 PECS,代表 Producer Extends、Consumer Super。

    在这个 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);
    }
    

    &lt;? extends E&gt; 不能在这里使用,因为 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

    但是,在您的第三个示例中,您获取和放入同一个列表,所以我认为您根本不能使用通配符。

    这样编译:

    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 无论是什么都必须相同。

    【讨论】:

    • 感谢您的解释。在第二种情况下,代码工作的地方是 List 消费者还是生产者?我已经编辑了问题并添加了另一个案例,请帮助解决第三种情况。
    • 感谢您的回答。好吧,我想通过传递类型列表(比如 Number)来限制对方法 insertAndProcess() 的调用,只是因为它对该类型使用了一些特定的实现,所以我需要使用 。我想,我现在必须在课堂上限制它。
    【解决方案2】:

    泛型有时有点困难。尝试一步一步分析。

    想象一下 - 在您的第一个示例(未编译)中 - 您调用 put 方法并将类型变量 E 替换为 Number。当您使用 List&lt;? extends E&gt; 约束输入参数 list 时,它可能是 List&lt;Integer&gt; 例如。参数value 的类型是E(请记住:ENumber),所以它可以是Double 例如。这是不允许的,因为您会尝试将Double 添加到List&lt;Integer&gt; 中。

    在第二个示例中,您只有输入参数list 的约束。没有其他参数依赖于E。设置和进入列表将始终有效,因为相关元素必须属于同一类型。

    解决方案:

    始终提醒首字母缩略词PECS。见Wildcard (Java)。这意味着您应该将put 方法声明如下(在您的第一个示例中):

    public <E> void put(List<? super E> list, int toPos, E value) {
        putHelper(list, toPos, value);
    }
    

    【讨论】:

    • 谢谢。清晰的解释有很大帮助。我读过关于 PECS 的文章,但我声明了 put(List extends E>) 以便它可以与 insertAndProcess() 一起使用,请参阅 EDITED 问题。任何帮助表示赞赏。
    • @shujin 当生产消费你不能声明extends或者super。所以你必须坚持List&lt;E&gt;
    【解决方案3】:

    List&lt;E&gt; 将只接受 E 类对象,而 List&lt;? extends E&gt; 可以接受 E 的任何子类。

    当你说一个方法只能接受 List&lt;E&gt; 传递 List&lt;? extends E&gt; 时,编译器会感到困惑,因为它不知道你的列表将实际包含哪种类型的 Object。

    注意,在java中,这是非法的:

    List<E> list = new ArrayList<? extends E>();
    

    例如List&lt;Number&gt; list = new ArrayList&lt;Integer&gt;();

    在第二个示例中,您的列表需要 E 的任何子类,并且您传递的是 E 对象列表,因此它是允许的。 例如。 List&lt;? extends Number&gt; list = new ArrayList&lt;Integer&gt;();

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-27
      相关资源
      最近更新 更多