2011-05-30 81 views
2

在以下示例代碼中,兩個類EventAEventB都實現了interface Historical。當其中一個對象作爲參數傳遞時,Java可以自動將EventAEventB轉換爲Historical,如下面的examineEvent方法所示。但是,當引入泛型時,Java不再可以投射,從List<EventA>List<Historical> - 除非使用List<? extends Historical>聲明目標函數(在此例中爲findClosestValidEventIndex)。Java:使用接口指針進行泛型轉換

有人可以解釋爲什麼這一定是?在我看來,在通用中使用接口應該自動暗示<? extends Interface>

public class SampleApplication { 

    public interface Historical { 
     public DateTime getDate(); 
    } 

    public static class EventA implements Historical { 
     private DateTime date; 
     @Override 
     public DateTime getDate() { 
     return date; 
     } 
    } 

    public static class EventB implements Historical { 
     private DateTime date; 
     @Override 
     public DateTime getDate() { 
     return date; 
     } 
    } 

    private static int findClosestValidEventIndex(List<Historical> history, DateTime when) { 
     // do some processing 
     return i; 
    } 

    private static int examineEvent(Historical event){ 
     return j; 
    } 

    public static void main(String[] args) { 
     DateTime target = new DateTime(); 
     // AOK 
     EventA a = new EventA(target); 
     int idy = examineEvent(a); 
     // Type Error --- Unless 
     List<EventA> alist = new ArrayList<EventA>(); 
     int idx = findClosestValidEventIndex(alist, target); 
    } 
} 

回答

6

因爲List<EventA>List<Historical>。試想一下:

List<EventA> list = ...; 
List<Historical> h = (List<Historical>) list; 
h.add(new EventB()); //type-safety of list is compromised 
for (EventA evt : list) { // ClassCastException - there's an EventB in the lsit 
    ... 
} 

List<? extends Historical>意味着「歷史的一個亞型特異性的名單」,你可以不加任何東西,因爲在編譯的時候你不知道是什麼類型。

+0

好的,我明白了你的觀點。謝謝! – Colin 2011-05-31 17:37:43

3

它不能總是使用extends,因爲有時您可能需要不同的行爲。

當通用對象用作生產者時,需要協方差(extends)。當通用對象用作消費者時,您需要使用逆變函數(super)。實用的規則是「生產者 - 延長,消費 - 超級」:

public void getEventFromHistory(List<? extnds Historical> history) { 
    Historical e = history.get(0); // history is a producer of e 
} 

public void addEventAToHistory(List<? super EventA> history) { 
    history.add(new EventA()); // history is a consumer 
} 

// Possible usage 
List<EventA> sequenceOfEventsA = ...; 
addEventAToHistory(sequenceOfEventsA); 

List<Historical> history = ...; 
addEventToHistory(history); 
+0

你提出的規則聽起來很有用,但我不確定我是否理解'? super'。這基本上是聲明一個弱類型的對象指針?在訪問這種類型的泛型方法時,你仍然可以使用超類的方法嗎? – Colin 2011-05-31 17:35:16

+0

@Colin:沒有得到您的評論。也許更新的代碼示例更有意義。 – axtavt 2011-06-01 08:41:48

+0

沒問題,我想我明白你的觀點。 – Colin 2011-06-01 21:45:57

1

你不能在綁定到子類來這勢必超類的集合的方法參數的集合傳遞。

這是因爲聲明按照定義列出歷史記錄意味着它是一個能夠容納任何延伸Historical的列表。這意味着您可以將EventA或EventB或這些類型的組合添加到此集合中。

但是列表<EventA>表示僅支持EventA類型的對象或其子類型的列表。這樣的列表不能包含添加到其中的EventB。

因此,Java不允許你通過一個List <EventA>作爲有效參數,其期望List <歷史>方法參數。畢竟,儘管HistoricalA和EventA以及HistoricalB和EventB之間存在繼承關係,但列表<歷史>與列表<事件A >之間沒有這種關係。