[英]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)
方法可以接受任何實現接口的方法。
假設您有一些名為Foo
和Bar
類,它們都實現了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
Integer
, Float
的引用:
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.