簡體   English   中英

Java中的接口和泛型

[英]Interfaces and generics in Java

我有代碼:

Set<? extends Notifiable> notifiables;

通知是一個界面。 我不明白上面的代碼和之間的區別:

Set<Notifiable> notifiables;

如果Notifiable是一個類,那么我就會理解它的區別,第一個代碼將允許Notifiable和Notifiable的任何子類,而第二個代碼只允許Notifiable(而不是任何子類)

由於您不能擁有接口實例,我可以在集合中添加/ etc? 在我看來,只有兩個選項,或者任何實現可通信的東西(在這種情況下,如何與第一個代碼不同),或者只有“Notifiable”的實例,它們不存在,因此沒有任何東西(這是毫無意義的應拋出編譯時錯誤)。

Set<Notifiable> 可以包含實現Notifiable的類的實例。 它不局限於只持有的具體類型實例Notifiable (和你是正確的,有沒有這樣的事情)。 但是Set<Notifiable>保證它可以包含任何類型的Notifiable ,因為它有一個add(Notifiable)方法可以接受任何實現接口的方法。

假設您有一些名為FooBar類,它們都實現了Notifiable 如果你創建一個Set<Foo> - 即一個允許包含Foo實例及其子類型的集合 - 你不能將它傳遞給一個采用Set<Notifiable>方法,因為該方法可能會添加不是Foo實例,比如Bar

public void addABar(final Set<Notifiable> notifiables) {
    notifiables.add(new Bar());  // OK, since Bar is a subtype of Notifiable
}

public void wontWork() {
  final Set<Foo> foos = new HashSet<>();
  addABar(foos);  // Compile error, can't convert Set<Foo> to Set<Notifiable>
}

但有時你想要編寫一個方法,除了Set<Notifiable>之外, 還可以接受Set<Foo>Set<Bar>類的東西。 這就是通配符的來源.A Set<? extends Notifiable> Set<? extends Notifiable>保證其中的所有內容都是某種Notifiable ,但並不保證可以添加各種Notifiable ; 允許將其限制為子類型。 你不能在它上面調用add() ,因為該方法現在是add(? extends Notifiable)而不是add(Notifiable) ,你不能調用參數類型未知的方法。

您通常在不需要添加元素時使用它,但是您需要查看現有元素並在它們上調用Notifiable接口方法,並且您希望允許調用者傳遞子集等子集,例如Set<Foo>

例如:

public void notifyAll(final Set<? extends Notifiable> notifiables) {
    for (final Notifiable notifiable : notifiables) {
        notifiable.notify();
    }
}

public void example() {
    final Set<Foo> foos = whatever();
    notifyAll(foos);  // OK, since a Set<Foo> is a Set<? extends Notifiable>
}

如果notifyAll()采用Set<Notifiable> ,您將無法將foos傳遞給它。

讓我們使用一個更直接的例子:

Set<? extends Serializable> serializables;

這個聲明了一個變量,它可以保存對一Set IntegerFloat的引用:

 serializables = new HashSet<Serializable>(); // valid
 serializables = new HashSet<Number>(); // this is valid as well
 serializables = new HashSet<Integer>(); // valid

另一方面這個:

Set<Serializable> serializables;

但是只能保存一Set Serializable對象:

serializables = new HashSet<Serializable>();
serializables = new TreeSet<Serializable>();

所以這個將是一個編譯器錯誤:

List<Serializable> numbers = new ArrayList<Integer>();

推論:

如果你想要一個可以容納Notifiable 任何子類型的字段,那么使用:

Set<Notifiable> notifiables = new HashSet<Notifiable>();

如果你想限制可以使用的Notifiable的子類型,那么這是要走的路:

Set<? extends Notifiable> notifiables = new HashSet<MyNotifiable>();

附錄:

這是完全合法的,所以你可以在你認為合適的時候改裝你的Set

Set<? extends Notifiable> notifiables = new HashSet<NotifiableA>();
notifiables  = new HashSet<NotifiableB>(); 

對於Collection類型中的一級泛型,角色是? extends ? extends通配符不是很重要,因為所有的Collection方法都是這樣的

E get(int index);
boolean add(E e);

它們實際上與:

? extends E get(int index);
boolean add(? extends E e);

由於Java子類型多態性規則。

但是,當子類型規則發揮作用時,通配符很重要:

    List<Integer> list = Arrays.asList(1);
    List<? extends Number> numSubTypeList = list; // Works
    List<Number> numList = list; // Illegal

同樣,對於多級泛型:

    Collection<Collection<Number>> col1 = new ArrayList<Collection<Number>>();
    col1.add(list); // Illegal

    Collection<Collection<? extends Number>> col2 = new ArrayList<Collection<? extends Number>>();
    col2.add(list); // Works

但是,即使col2的簽名也不適用於通用庫。 那些可能會期待Collection<? extends Collection<? extends Number>> Collection<? extends Collection<? extends Number>> Collection<? extends Collection<? extends Number>> ,其中col2是子類型。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM