2011-06-15 96 views
5

我無法將列表中的水果投射到列表中包含的水果子類。繼承和鑄造列表對象

public class Response { 
    private List<Fruit> mFruitList; 
    public List<Fruit> getFruitList() { 
     return mFruitList; 
    } 
} 

public class Fruit { 
} 

public class Orange extends Fruit { 
} 

List<Fruit> oranges = response.getFruitList(); 

我該如何投擲橙子,以便它是橙類的列表?這是一個糟糕的設計模式嗎?基本上我從一個水果列表的服務器獲取JSON響應。對於每個特定的Web服務調用,我都知道Fruit的子類會得到什麼,所以我需要適當地轉換該List。

+0

你有沒有試過'列表 oranges =(列表)response.getFruitList();'? – talnicolas 2011-06-15 16:34:44

+0

是的,我試了很多強制鑄造沒有積極的結果。我正在重新考慮我的方法。也許最好是對Response進行子類化併爲每個Fruit子類實現適當的List。響應的90%包含每個水果類相同的成員,但列表除外。 – 2011-06-15 16:49:08

+1

更新,共識是 - 子類化泛型。 – 2011-06-15 16:52:36

回答

7

如果你知道每個特定的調用,你會得到什麼水果的子類,那麼你應該使用泛型而不是鑄造列表。

public class Response<T extends Fruit> { 
    private List<T> mFruitList; 
    public List<T> getFruitList() { 
     return mFruitList; 
    } 
} 

Response<Orange> response = // create 
List<Orange> oranges = response.getFruitList(); 

編輯:模板我的意思是泛型類型。對不起,我現在有太多的C++

+0

模板的意思是什麼?我的理解是,Java沒有像C++那樣擁有相同的模板。 – 2011-06-15 16:44:45

+0

啊有趣。讓我試試這個。 。 。 – 2011-06-15 16:49:54

0

您應該投射列表,並測試每個元素是否爲instanceof Orange,並且在橘子測試後投射。這是「最佳實踐」。

3

typecasts背後的全部想法是能夠告訴編譯器:「嘿,我比你知道的更多。」在你的代碼中,編譯器不能安全地將List<Fruit>下載到List<Orange>,因爲它不知道列表在運行時將包含什麼。

如果你是絕對肯定該列表將只有Orange實例,它使你的代碼更容易管理下降,去爲它。

List<Orange> oranges = (List<Orange>) response.getFruitList(); 

編譯器會給你一個警告,當然,因爲你正在做一些它認爲你不應該這樣做。只要知道,如果你錯了,JVM可能會因爲拋出CastClassException而笑到最後!

+0

嗯,我會upvote,... – talnicolas 2011-06-15 16:45:19

+0

也許這將是更好的子類Response和實施適當的列表爲每個相應的水果子類? – 2011-06-15 16:46:49

+1

@Arch Stanton而不是繼承'Response',你也可以通用化它。 '公開課答覆 {私人列表水果;/*等* /}' – stevevls 2011-06-15 16:50:34

1

想象泛型就像一個列表可以包含哪些類型的對象的大門。因爲這種繼承和鑄造不能按照你期望的方式工作。在你給你的例子中,你可以把橙子和蘋果放在你的List<Fruit>。如果列表中同時包含蘋果和橙子,您如何將它投射到List<Orange>

如果你需要一個List<Orange>那麼爲什麼即使有List<Fruit>麻煩。如果你無論如何都明確地投射它,並且你確切地知道它包含了什麼,它可能是一個不必要的抽象。

如果您正在使用的API時,你不能改變,但你確切地知道它包含什麼,那麼你應該通過循環利用instanceof檢查,以確保公正和明確地投各Fruit實例Orange當你需要的Orange API。

0

鑑於

List<Fruit> getFruits() {...} 

你不能強制轉換

List<Orange> oranges = (List<Orange>) getFruits(); 

由於類型擦除,在運行時getFruits的類型只是名單。編譯器甚至不會讓你做下降(我有疑問,所以我在回答之前在Eclipse中嘗試過)。

你可以告訴你的列表將包含水果的某些子類的編譯器,在這種情況下,你需要使用通配符你方法:

List<? extends Fruit> getFruits() {...} 

然後投成爲可能,但有型安全警示:

@SuppressWarnings("unchecked") 
List<Orange> oranges = (List<Orange>) getFruits(); 

鑑於getFruits 的運行時類型爲列表,你可以丟棄泛型類型信息和使用不安全的分配新建分配FY:

@SuppressWarnings("unchecked")  
List<Orange> oranges = (List) getFruits(); 

也許因爲它清楚地指出你的意圖,但需要更多的系統資源會是一個更優雅的方式:

List<Orange> oranges = Arrays.asList((Orange[])getFruits().toArray()) 

陣列在Java中保存運行時的類型信息,所以劇組是有效的,「安全「從編譯器的角度來看,但如果你在水果籃中傳遞一些蘋果,它會拋出一個運行異常。