2009-07-15 21 views
7

爲什麼java泛型如此棘手?我想我終於明白了,但是eclipse在下面的somOtherMethod中使用下面的getOuterList方法中的一行給出了一個錯誤。java中的簡單通用列表

protected List<?> getOuterList() { 
    // blah blah 
} 

protected List<? extends Object> getOuterList() { 
    // blah blah 
} 

protected void someOtherMethod() { 
    ... 
    getOuterList().add((MyObject)myObject); //compile error 
    ... 
} 

UPDATE:好 - 所以我現在明白了錯誤。這是我對List<?>List<? extends SomeObject>確實意味着什麼缺乏瞭解。在前一種情況下,我認爲這意味着可以包含任何內容的列表。在後一種情況下,我認爲它是擴展SomeObject的一堆對象的列表。我的理解的正確表示將只是List<Object>List<SomeObject>(不包括延伸)。我認爲延伸幫助我解決了一個他們不知道的問題。所以這裏是我真正的問題所在:

public interface DogKennel { 
    public List<Dog> getDogs(); 
} 

public class GreyHoundKennel implements DogKennel { 

    protected List<GreyHound> greyHounds; 

    public List<GreyHound> getGreyHounds() { 
    return this.greyHounds; 
    } 

    public List<Dog> getDogs() { 
    // Is there no way to handle this with generics 
    // w/out creating a new List? 
    return getGreyHounds(); //compiler error 
    } 

} 
+0

什麼是錯誤? – jjnguy 2009-07-15 18:16:23

回答

1

你絆倒的事實,Java泛型不在類型參數多態性。

通過您的代碼片段交談,讓我們拉相距例如:

protected List<GreyHound> greyHounds; // List<GreyHound> is fine 

/** This method returns a lovely List of GreyHounds */ 
public List<GreyHound> getGreyHounds() { 
    return this.greyHounds; 
} 

/** Here is the problem. A List<GreyHound> is not a List<Dog> */ 
public List<Dog> getDogs() { 
    return getGreyHounds(); //compiler error 
} 

所以你原來的評論是正確的。這兩個列表完全不同,它們之間沒有繼承關係。所以,我建議你調查這兩個選項:

  1. 嘗試返回一個新的列表,如您在評論中所建議的。例如,return new ArrayList<Dog>(this.greyHounds);

  2. 您是否確實需要保留特定品種的狗列表?也許你應該定義數據成員爲你添加特定的GreyHounds的List<Dog>。例如,protected List<Dog> greyHoundsOnly;您可以通過對象的外部接口來管理犬舍中允許哪些狗。

除非你有一個很好的理由讓一個特定類型的列表中,我會認真考慮的選項2.

編輯:充實了上述我建議的選項:

選項1:返回新列表。優點:簡單明瞭,你可以得到一個類型化的列表,它消除了線程安全問題(不向世界揭示內部引用)。缺點:似乎是性能成本。

// Original code starts here. 
public interface DogKennel { 
    public List<Dog> getDogs(); 
} 

public class GreyHoundKennel implements DogKennel { 

    protected List<GreyHound> greyHounds; 

    public List<GreyHound> getGreyHounds() { 
    return this.greyHounds; 
    } 
// Original code ends here 

    public List<Dog> getDogs() { 
    // This line eliminates the thread safety issue in returning 
    // an internal reference. It does use additional memory + cost 
    // CPU time required to copy the elements. Unless this list is 
    // very large, it will be hard to notice this cost. 
    return new ArrayList<Dog>(this.greyHounds); 
    } 

} 

選項2:使用不同的數據表示。優點:多態性更好,返回最初目標的通用列表。缺點:這是一個略有不同的架構,可能不適合原來的任務。

public abstract class DogKennel { 
    protected List<Dog> dogs = new ArrayList<Dog>(); 
} 

public class GreyHoundKennel extends DogKennel { 

    // Force an interface that only allows what I want to allow 
    public void addDog(GreyHound greyHound) { dogs.add(greyHound); } 

    public List<Dog> getDogs() { 
    // Greatly reduces risk of side-effecting and thread safety issues 
    // Plus, you get the generic list that you were hoping for 
    return Collections.unmodifiableList(this.dogs); 
    } 

} 
+0

選項#1:這是一個性能問題。最好不要使用泛型。 選項#2:「我真的需要保留特定品種的狗列表嗎?」不,但我認爲那是打字清單給你買的?如果我不能那麼做,那麼泛型的意義何在。 我的結論:泛型是浪費時間。 – andersonbd1 2009-07-16 13:46:25

0

泛型類型?意思是「某種特定類型,但我不知道哪一種」。什麼使用?本質上是隻讀的,因爲無法知道實際的類型就不能寫入它。

3

您是說該方法返回某個未知類型的「List」(您無法添加,因爲您無法保證所添加的內容是該類型的子類型)。實際上,你想說的是,一個「你想要的任何類型的List」,所以你必須使該方法一般:

protected <T> List<T> getOuterList() { 
    // blah blah 
} 

好吧,我只是看着您的更新:

這一切都取決於什麼你打算能夠處理getDogs()的結果。如果您不打算能夠將任何項目添加到列表中,則getDogs()應該返回類型List<? extends Dog>,然後問題就會解決。

如果要能夠添加東西給它,並通過類型List<Dog>這意味着你可以添加任何種類的Dog它,那麼在邏輯上這個列表不能是相同的列表爲greyHounds,因爲greyHounds有型List<GreyHound>Dog對象不應該進去。

這意味着您必須創建一個新列表。請記住,新列表的任何更改都不會反映在原始列表greyHouds中。

+0

這不是非常有用,除非它返回一個空列表(或空列表)。 – 2009-07-15 19:44:31

+0

歡呼聲,泛型是我**正在尋找的東西。 – 2014-05-04 16:50:53

3

此聲明:

List<?> getOuterList() { } 

告訴編譯器:「我真的不知道我要找回什麼樣的名單」。然後你基本上執行

list<dunno-what-this-is>.add((MyObject)myObject) 

它不能將MyObject添加到它不知道它是什麼類型的東西的列表。

此聲明:

protected List<? extends Object> getOuterList() { ... } 

告訴編譯器「這是事情是對象的子類型列表」。所以再次,當然你不能投到「MyObject」,然後添加到對象列表。因爲所有編譯器都知道該列表可以包含對象。

但是可以,做這樣的事情:

List<? super MyObject>.getOuterList() { ... } 

,然後成功添加爲MyObject。那是因爲現在編譯器知道List是MyObject的列表,或者MyObject的任何超類型,所以它肯定可以接受MyObject。

編輯:至於你DogKennel例如,這個代碼片段,我認爲你想要做什麼:

protected List<GreyHound> greyHounds; 

// We only want a List of GreyHounds here: 
public List<GreyHound> getGreyHounds() { 
    return this.greyHounds; 
} 

// The list returned can be a List of any type of Dog: 
public List<? extends Dog> getDogs() { 
    return getGreyHounds(); 
} 
0

已經有一個可接受的答案,但是,請考慮下面的代碼修改。

public interface DogKernel { 
    public List<? extends Dog> getDogs(); 
} 

public class GreyHoundKennel implements DogKernel { 
    protected List<GreyHound> greyHounds; 

    public List<GreyHound> getGreyHounds() { 
     return this.greyHounds; 
    } 

    public List<? extends Dog> getDogs() { 
     return getGreyHounds(); // no compilation error 
    } 

    public static void main(String[] args) { 
    GreyHoundKennel inst = new GreyHoundKennel(); 
    List<? extends Dog> dogs = inst.getDogs(); 
    } 
} 

Java泛型確實被破壞了,但沒有被破壞。 順便說一句,斯卡拉通過提供差異處理以非常優雅的方式解決了這個問題。

UPDATE ----------

請考慮更新的代碼片段。

public interface DogKennel<T extends Dog> { 
    public List<T> getDogs(); 
} 

public class GreyHoundKennel implements DogKennel<GreyHound> { 
    private List<GreyHound> greyHounds; 

    public List<GreyHound> getDogs() { 
     return greyHounds; // no compilation error 
    } 

    public static void main(String[] args) { 
     GreyHoundKennel inst = new GreyHoundKennel(); 
     inst.getDogs().add(new GreyHound()); // no compilation error 
    } 
}