【问题标题】:Java Generics and templatesJava 泛型和模板
【发布时间】:2015-11-07 18:19:56
【问题描述】:

我有以下Java 类定义:

import java.util.*;

public class Test {

static public void copyTo(Iterator<? extends Number> it, List<? extends Number> out) {
    while(it.hasNext())
        out.add(it.next());
}
public static void main(String[] args) {
    List<Integer> in = new ArrayList<Integer>();
    for (int i = 1; i <= 3; i++) {
        in.add(i);
    }
    Iterator<Integer> it = in.iterator();
    List<Number> out = new ArrayList<Number>();
    copyTo(it, out);
    System.out.println(out.size());
}

}

就是这样,我在Java 中使用wildcards 定义了方法copyTo。我定义List&lt;Number&gt; outIterator&lt;Integer&gt; it。我的想法是我可以将迭代器定义为 Iterator&lt;? extends Number&gt; 并且类型匹配。然而事实并非如此:

Test.java:13: error: no suitable method found for add(Number)
            out.add(it.next());
               ^
    method List.add(int,CAP#1) is not applicable
      (actual and formal argument lists differ in length)
    method List.add(CAP#1) is not applicable
      (actual argument Number cannot be converted to CAP#1 by method invocation conversion)
    method Collection.add(CAP#1) is not applicable
      (actual argument Number cannot be converted to CAP#1 by method invocation conversion)
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Number from capture of ? extends Number
1 error

所以我继续为copyTo 方法定义了另一个定义:

static public void copyTo(Iterator<? super Integer> it, List<? super Integer> out) {
        while(it.hasNext())
            out.add(it.next());
    }

它也不起作用。在这种情况下使用wildcards 的正确说法是什么?

【问题讨论】:

    标签: java templates generics types wildcard


    【解决方案1】:

    首先,您想通过向方法本身添加类型变量来施加约束,因为使用通配符您不能在两个参数之间施加约束,那么您必须考虑方法中涉及的类型的差异:

    • 您希望将Iterator&lt;X&gt; 作为输入,其中X 至少是您要复制的数字类型(或子类型)
    • 您希望输出一个 List,其中 Y 最多是数字类型(或超类型)的类型

    这些约束是不同的,必须用不同的方式表达:

    static public <T> void copyTo(Iterator<? extends T> it, List<? super T> out) {
    while(it.hasNext())
        out.add(it.next());
    }
    

    这基本上是“我接受TIteratorT 的子类型,并输出到T 的列表或T 的超类型”

    【讨论】:

      【解决方案2】:

      如果方法签名涉及两个或多个通配符,并且您的方法的逻辑要求它们相同,则需要使用泛型类型参数而不是通配符。

      static public <T extends Number> void copyTo(Iterator<? extends T> it, List<? super T> out) {
          while(it.hasNext())
              out.add(it.next());
      }
      

      这里我使用了 PECS(生产者扩展,消费者超级)。 out 正在消耗Ts(所以super),而迭代器正在生成Ts,所以extends

      编辑

      正如@Cinnam 在 cmets 中正确指出的那样,您可以侥幸逃脱

      static void copyTo(Iterator<? extends Integer> it, List<? super Integer> out)
      

      这些签名实际上是等效的,因为Integer 是最终的,因此任何扩展Integer 的类的超类必须是Integer 的超类。

      但是,就编译器而言,这两个签名并不等价。你可以试试这个

      static <T extends Number> void copyTo1(Iterator<? extends T> it, List<? super T> out) {
          copyTo2(it, out); // doesn't compile
      }
      
      static void copyTo2(Iterator<? extends Integer> it, List<? super Integer> out) {
         copyTo1(it, out);
      }
      

      编译,说明就编译器而言,带type参数的版本更通用。

      【讨论】:

      • 如果是void copyTo(Iterator&lt;? extends Integer&gt; it, List&lt;? super Integer&gt; out)还不够吗?
      • @Cinnam 是的,你是对的。这是一个更好的解决方案。
      猜你喜欢
      • 2023-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多