[英]What is the use and point of unbound wildcards generics in Java?
我不明白未綁定通配符泛型的用途是什么。 上限為<? extends Animal>
綁定通配符泛型 <? extends Animal>
非常有意義,因為使用多態性我可以處理該類型或集合。 但是擁有可以是任何類型的泛型有什么意義呢? 它不會違背泛型的目的嗎? 編譯器沒有發現任何沖突,並且在類型擦除之后就像沒有使用泛型一樣。
當您的方法實際上並不關心實際類型時,未綁定類型可能會很有用。
一個原始的例子是這樣的:
public void printStuff(Iterable<?> stuff) {
for (Object item : stuff) {
System.out.println(item);
}
}
由於PrintStream.println()
可以處理所有引用類型(通過調用toString()
),所以我們不在乎該Iterable
的實際內容是什么。
呼叫者可以傳入List<Number>
或Set<String>
或Collection<? extends MySpecificObject<SomeType>>
Collection<? extends MySpecificObject<SomeType>>
。
還要注意,根本不使用泛型(使用原始類型進行調用)會產生完全不同的效果:這會使編譯器處理整個對象 ,就好像泛型根本不存在一樣 。 換句話說:不僅會忽略類的類型參數,還會忽略方法上的所有泛型類型參數。
另一個重要的區別是您不能將任何(非null
)值添加到Collection<?>
,但可以將所有對象添加到原始類型Collection
:
這不會編譯,因為c
的類型參數是未知類型(=通配符?
),所以我們不能提供保證可分配給該值的值( null
除外,該值可分配給所有引用)類型)。
Collection<?> c = new ArrayList<String>();
c.add("foo"); // compilation error
如果不使用type參數(即使用原始類型),則可以向集合中添加任何內容 :
Collection c = new ArrayList<String>();
c.add("foo");
c.add(new Integer(300));
c.add(new Object());
請注意,編譯器會警告您不要使用原始類型,尤其是出於這個原因:它會刪除所有與泛型相關的類型檢查。
當您需要執行instanceof
檢查時。
您不能像這樣進行參數化:
Object value;
if (value instanceof List<String>) {
// ...
}
所以你也是:
Object value;
if (value instanceof List<?>) {
// ...
}
對於綁定的通配符,有(很少)完全正確的用例。 SDK包含其中一些。
一個示例是一種對任何類型的列表執行確定操作並且不返回Collections
rotate
的任何方法的方法:
static void rotate(List<?> list, int distance)
另一個示例是當您要列出類的可能構造函數時,方法是:
Constructor<?>[] getConstructors()
在這里甚至不可能使用泛型,因為根據定義,數組將包含不同的構造函數,每個構造函數都有自己的實際類。 相比之下,API確實使用通用簽名來獲取一個構造函數: Constructor<T> getConstructor(Class<?>... parameterTypes)
。
結論是,即使它主要用於與較早的代碼兼容,但仍有某些地方使用未綁定的通配符泛型是正確的方法。
請允許我重新表述這個問題:
“ List<Object>
和List<?>
什么區別?”
答案是List<?>
更具限制性。 它告訴我們,我們有一堆某種類型的對象,但是該類型不一定是Object
。
由於我們不知道該類型是什么,因此我們根本無法添加到列表中-我們添加的任何內容都可能是錯誤的類型。 實際上,我們不能傳遞?
任何參數?
輸入任何方法,而不僅僅是add()
。
從好的方面來說,當我們指定方法采用List<?>
,它可以采用List<String>
或List<Integer>
或任何其他List<>
。 List<Object>
只能采用List<Object>
。
雖然使用原始類型意味着您不了解泛型(因為您很懶或者代碼是很久以前編寫的),但是使用<?>
意味着您了解泛型並明確強調您的代碼可以與任何類型的對象一起使用。
List<Object> 是一個可以包含任何對象的列表,例如 l[0] 可能是一個整數,l[1] 可能是一個字符串等。 List<?> 可能是一個 List<Integer> 或 List<String>等。如果是List<Integer>,則只存儲Integers,如果是List<String>,則只存儲Strings。
在包裝不使用泛型的舊代碼(基本上是Collections)時,使用無界通配符才有意義,AFAIK。
如果您看一下使用這種泛型可以做什么,那么基本上沒有什么 。 如果您有一個收藏集,那么您將無法添加任何內容;如果您嘗試讀取某些內容,則總會得到一個Object
,依此類推。
反過來,這有助於確保您將以類型安全的方式處理數據,而使用原始類型將導致編譯器忽略您可能造成的任何混亂。
通過通配符參數化類型的引用變量可訪問/不可訪問哪些方法和字段? 來自Angelika Langers的Java泛型常見問題解答可能是您感興趣的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.