2011-05-30 75 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 >之间没有这种关系。