2015-11-07 71 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是的,你是對的。這是一個更好的解決方案。 –