2015-11-07 73 views
1

我有以下Java类定义: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<Number> out,但Iterator<Integer> it。我的想法是我可以将迭代器定义为Iterator<? extends Number>,那样会匹配。但是,这种情况并非如此:

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的正确说法是什么?

回答

3

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

    要作为输入
  • 一个Iterator<X>其中X至少要复制的数值类型(或子类型)
  • 的类型要用作输出一个列表,其中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正在消耗T s(所以super),而迭代器正在生成T s,所以extends

编辑

由于@Cinnam正确的评论所指出的,你可以用

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

这个测试这样做编译,可见只要编译器而言,与类型参数的版本是比较一般。

+0

如果它是'void copyTo(Iterator <?扩展整数>它,列表<?超整型> out)'? – Cinnam

+0

@Cinnam是的,你是对的。这是一个更好的解决方案。 –