3

以下代碼不能在Eclipse中編譯。它說: 「在美國廣播公司型的方法putHelper(列表,INT,E)是不適用的參數(列表< .capture#8的擴展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方法中將類型參數更改爲Li st <。? super E>,當我嘗試從另一個接受列表的方法調用put()方法時,Eclipse不編譯它。它說:「類型Abc中放置的方法(列表<。?super E>,int,E)不適用於參數(列表<。捕獲#6 - 擴展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>?

回答

2

這是因爲Get和Put原則也被縮寫PECS它代表生產者的延伸消費超聞名。

這是在這太問題解釋說: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

但是,在你的第三個例子中,你們都得到並放入同一個列表中,所以我認爲你根本不會使用通配符。

這編譯:

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必須是相同的,無論它是什麼。

+0

謝謝您的解釋。在代碼工作的第二種情況下,是List消費者還是生產者?我編輯了這個問題並添加了另一個案例,請幫助第三個案例。 – shujin

+0

@shujin編輯我的回答以解決新問題 – dkatzel

+0

謝謝你的回答。那麼,我想通過傳遞類型列表(比如Number)來限制對方法insertAndProcess()的調用,只是因爲它使用了某種特定的類型實現,所以我需要使用。我想,現在我不得不在課堂上限制它。 – shujin

0

List<E>將只接受E類對象,其中List<? extends E>可以接受任何子類的E。

當你說一個方法可以只通過List<E>傳遞List<? extends E>會混淆編譯器,因爲它不知道列表實際上將包含哪種類型的對象。

注意,在Java中,這是非法的:

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

例如List<Number> list = new ArrayList<Integer>();

在第二個示例中,您的列表需要E的任何子類,並且您傳遞的是E對象的列表,因此它允許。 例如List<? extends Number> list = new ArrayList<Integer>();

1

泛型有時會有點困難。嘗試一步一步分析它。

想象一下 - 在您的第一個示例中(不編譯) - 您調用put方法,其中E類型變量替換爲Number。由於您使用List<? extends E>限制輸入參數list,因此它可能是List<Integer>。參數value的類型是E(記住:ENumber),所以它可以是例如Double。這一定是不允許的,因爲你會嘗試將Double加入List<Integer>

在第二個示例中,您只對輸入參數list有約束。沒有其他參數依賴於E。設置和獲取列表將始終有效,因爲所討論的元素必須是相同的類型。

A液:

時刻提醒的縮寫PECS。見Wildcard (Java)。這意味着您應該聲明put方法如下(在您的第一個示例中):

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

謝謝。明確的解釋幫助了很多。我曾閱讀過PECS,但我宣佈放置(列表),以便它可以與insertAndProcess()一起使用,請參閱EDITED問題。任何幫助表示讚賞。 – shujin

+1

@shujin當_producing_和_consuming_時,你不能聲明_extends_和_super_。所以你必須堅持'列表'。 – Seelenvirtuose