[英]Java generics super keyword
我經歷了這些話題
但是,我似乎仍然對super
關鍵字感到迷茫:
當我們像這樣聲明一個集合時:
List<? super Number> list = null; list.add(new Integer(0)); // this compiles list.add(new Object()); // this doesn't compile
不應該相反 - 我們有一個包含一些對象(未知類型)的列表,它們是Number
父對象。 所以Object
應該適合(因為它是Number
的父級),而Integer
不應該。 出於某種原因,情況正好相反。
如果我們有以下代碼
static void test(List<? super Number> param) { param.add(new Integer(2)); } public static void main(String[] args) { List<String> sList = new ArrayList<String>(); test(sList); // will never compile, however... }
編譯上面的代碼是不可能的(我的理智表明這是正確的行為),但基本邏輯可以證明相反:
String is Object, Object is superclass of Number. So String should work.
我知道這很瘋狂,但這不是他們不允許<S super T>
構造的原因嗎? 如果是,那為什么<? super T>
<? super T>
允許嗎?
有人可以幫我恢復這個邏輯鏈的缺失部分嗎?
List<? super Number>
的有界通配符 List<? super Number>
可以捕獲Number
及其任何超類型。 由於Number extends Object implements Serializable
,這意味着目前只有List<? super Number>
List<? super Number>
是:
List<Number>
List<Object>
List<Serializable>
請注意,您可以add(Integer.valueOf(0))
到上述任何類型中。 但是,您不能add(new Object())
到List<Number>
或List<Serializable>
,因為這違反了通用類型安全規則。
因此,它是不正確的,你可以add
任何超Number
到List<? super Number>
List<? super Number>
; 這根本不是有界通配符和捕獲轉換的工作方式。 你沒有聲明一個List<? super Number>
List<? super Number>
因為你可能想給它添加一個Object
(你不能!); 你這樣做是因為你想向它添加Number
對象(即它是Number
的“消費者”),而簡單的List<Number>
限制太多。
extends
,消費者super
new Integer(0)
與valueOf
等對於第一部分List<Number>
適合List<? super Number>
List<? super Number>
但您不能將Object
添加到List<Number>
。 這就是為什么您不能將Object
添加到List<? super Number>
List<? super Number>
。
另一方面,您可以將Number
每個子類(包括Number
)添加到您的列表中。
對於第二部分, String
是一個Object
,但String
不是Number
的超類。
如果它像這樣工作,因為每個類都是Object
的子類, super
將沒有意義。
List<? super Number>
List<? super Number>
:List<Object>
List<Object>
會起作用Object
適合<? super Number>
<? super Number>
Number
任何子類型添加到List<Object>
String
,您唯一確定的是您可以添加Number
任何子類。List<Number>
:
List<Number>
會起作用Number
適合<? super Number>
<? super Number>
Number
任何子類型添加到List<Number>
List<Integer>
(或Number
任何子類):
List<Integer>
不起作用Number
的子類,所以它正是我們想要避免的Integer
適合Number
您也無法在List<Integer>
添加Number
任何子類(例如Float
)super
並不意味着子類。List<String>
(或不延伸的任何類Number
,也沒有在的“超級層次” Number
(即Number
和Object
):
List<String>
不起作用String
不適合Number
“超級層次結構”String
適合Object
(它是Number
的超類),您也不能確保能夠將Number
添加到包含Number
超類之一的任何子類的List
中)super
並不意味着超類之一的任何子類,它只意味着超類之一。它是如何工作的?
您可以說,只要您可以使用鍵入的List
添加Number
任何子類,它就會尊重super
關鍵字。
我有一段時間沒有得到它。 此處的許多答案和其他問題具體顯示了某些用法在何時何地出現錯誤,但並沒有說明為什么。
這就是我最終得到它的方式。 如果我有一個將Number
s 添加到List
的函數,我可能想添加MySuperEfficientNumber
類型的它們,這是我自己的自定義類,它實現了Number
(但不是Integer
的子類)。 現在調用者可能對MySuperEfficientNumber
,但只要他們知道將添加到列表中的元素視為僅比Number
更具體的元素,他們就可以了。
如果我將我的方法聲明為:
public static void addNumbersToList(List<? extends Number> numbers)
然后調用者可以傳入一個List<Integer>
。 如果我的方法添加一個MySuperEfficientNumber
在2002年底numbers
,那么調用者將不再有一個List
的Integer
S和下面的代碼是行不通的:
List<Integer> numbers = new ArrayList<Integer>();
addNumbersToList(numbers);
// The following would return a MySuperEfficientNumber not an Integer
Integer i = numbers.get(numbers.size()-1)
顯然這是行不通的。 錯誤將在addNumbersToList
方法內。 你會得到類似的東西:
The method add... is not applicable for the arguments (MySuperEfficientNumber)
因為numbers
可以是任何特定類型的Number
,不一定與MySuperEfficientNumber
兼容。 如果我翻轉聲明以使用super
,該方法將無錯誤地編譯,但調用者的代碼將失敗:
The method addNumbersToList(List<? super Number>)... is not applicable for the arguments (List<Integer>)
因為我的方法是說,“不要認為你的List
可以比Number
更具體。我可能會在列表中添加各種奇怪的Number
,你只需要處理它。如果你想將它們視為比Number
更通用的東西——比如Object
那很好,我保證它們至少是Number
s,但如果你願意,你可以更一般地對待它們。”
而extends
是說,“我真的不在乎你給我什么樣的List
,只要每個元素至少是一個Number
。它可以是任何類型的Number
,甚至是你自己奇怪的、自定義的、虛構的Number
s. 只要他們實現了那個接口,我們就很好。我不會在你的列表中添加任何東西,因為我不知道你在那里使用的是什么實際的具體類型。”
List<? super Number>
List<? super Number>
意味着變量的引用類型表明我們有一個數字、對象或序列化的列表。
不能添加 Object 的原因是因為編譯器不知道這些類中的哪些在實際實例化對象的泛型定義中,所以它只允許您傳遞 Number 或 Number 的子類型,如 Double、Integer 和等等。
假設我們有一個返回List<? super Number>
List<? super Number>
。 方法內部對象的創建是從我們的角度封裝的,我們只是不能說它是不是這樣的:
List<? super Number> returnValue = new LinkedList<Object>();
或
List<? super Number> returnValue = new ArrayList<Number>();
因此,泛型類型可以是 Object 或 Number。 在這兩種情況下,我們都可以添加 Number,但只有在一種情況下我們才可以添加 Object。
在這種情況下,您必須區分引用類型和實際對象類型。
List<? super Number>
List<? super Number>
就是這樣一個List<AncestorOfNumber>
,我們可以隱式地將每個Number
為它的超類型AncestorOfNumber
。
考慮一下:需要什么泛型類型????
在下面的例子中?
InputStream mystream = ...;
void addTo(List<????> lsb) {
lsb.add(new BufferedInputStream(mystream));
}
List<BufferedInputStream> lb = new ArrayList<>();
List<InputStream> li = new ArrayList<>();
List<Object> lo = new ArrayList<>();
...
{ addTo(lb); addTo(li); addTo(lo); }
答案: ????
我們可以將BufferedInputStream
為任何東西,它非常相同或其祖先之一: ? super BufferedInputStream
? super BufferedInputStream
我可以舉一個非常簡單的例子。
public void add(List<? super Number> list) {
}
這將允許這些調用
add(new LinkedList<Number>());
和上面的所有數字像
add(new LinkedList<Object>());
但沒有低於層次結構所以不是
add(new LinkedList<Double>());
或
add(new LinkedList<Integer>());
因此,由於程序不清楚您給出的是帶有數字的列表還是帶有對象的列表,因此編譯器不能允許您向其中添加數字以上的任何內容。
例如,盡管對象會接受數字,但列表不會接受對象。 但由於尚不清楚,唯一有效的輸入是 Number 及其子類型。
這里有兩個角度:當涉及有界類型時,您可以將什么放入集合中,以及您可以從集合中獲得什么。
讓我們看看? extends Number
首先? extends Number
案例。 當定義了具有這樣邊界的集合時,我們知道:每個元素都有一個作為Number
的上限。 我們不知道確切的類型(可能是Integer/Long/etc
),但我們確實知道,它的上限是Number
。
所以從這樣的集合中讀取我們得到了一個Number
。 這是我們可以從中獲得的唯一保證類型。
禁止寫入此類集合。 但為什么? 我不是說過在我們閱讀時 - 我們總是會得到一個Number
,那么為什么要禁止寫入呢? 這里的情況稍微復雜一點:
List<Integer> ints = ....;
List<? extends Number> numbers = ints;
numbers.add(12D); // add a double in here
如果允許對numbers
,則可以有效地在List of Integers
添加一個Double
。
現在以您的示例為例:
List<? super Number> list = null;
list.add(new Integer(0));
list.add(new Object());
我們知道list
包含某個Number
超類型,例如Object
。
從這樣的列表中讀取將獲得特定類型X
,其中X
將是Number
的父級。 那會是什么? 你真的不可能知道。 它可以是理論上的MyNumber extends Number
,或者更簡單: Object
。 由於您無法確定,因此唯一安全的讀取方式是所有內容的超類型 - Object
。
有點奇怪的可能是:
List<? super String> list = ...;
String s = list.get(0); // fails, compiler does not care that String is final
寫入它稍微復雜一些,但只是稍微復雜一些。 記住我們所知道的在該list
:它是Number
擴展/實現的類型(如果它是一個接口),因此您始終可以為該超類型分配一個子類型(或Number
本身)。
Some type X
/ \
|
Number
/ \
|
Some type Y that we an put in here
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.