[英]Understanding upper and lower bounds on ? in Java Generics
我真的很難理解通配符參數。 我有幾個問題。
?
作為類型參數只能在方法中使用。 例如: printAll(MyList<? extends Serializable>)
我不能用?
作為類型參數。
我了解上限?
. printAll(MyList<? extends Serializable>)
手段:“ printAll
將打印MyList
如果它具有實施該對象Serialzable
接口”。
我對super
有點問題。 printAll(MyList<? super MyClass>)
手段:“ printAll
將打印MyList
如果它具有的對象MyClass
或延伸的任何類MyClass
(的后代MyClass
)”。
糾正我哪里出錯了。
簡而言之,只有T
或E
或K
或V
或N
可以用作定義泛型類的類型參數。 ?
只能在方法中使用
public void printAll(MyList<? super MyClass>){ // code code code }
根據 Ivor Horton 的書MyList<? super MyClass>
MyList<? super MyClass>
表示如果MyList
具有MyClass
對象或它實現的任何接口或類,我可以打印它。 也就是說, MyClass
是一個下界。 它是繼承層次結構中的最后一個類。 這意味着我最初的假設是錯誤的。
所以,假設MyClass
看起來像:
public class MyClass extends Thread implements ActionListener{ // whatever }
然后, printAll()
將打印如果
1.列表中有MyClass
對象
2.有對象Thread
或ActionListener
在List
因此,在閱讀了該問題的許多答案后,這是我的理解:
? extends T
? extends T
表示任何擴展T
類。 因此,我們指的是T
的孩子。 因此, T
是上限。 繼承層次結構中最上層的類
? super T
? super T
指任何類/接口是super
的T
。 因此,我們指的是T
所有父母。 因此, T
是下限。 繼承層次結構中最底層的類
?
作為類型參數只能在方法中使用。 例如:printAll(MyList<? extends Serializable>)
我不能用?
作為類型參數。
通配符 ( ?
) 不是正式的類型參數,而是可以用作類型參數。 在你給出的例子中, ? extends Serializable
? extends Serializable
被給定為一個類型參數的一般類型MyList
,所述的printAll
方法的參數。
static <T extends Serializable> void printAll(MyList<T> myList)
我了解上限
?
.printAll(MyList<? extends Serializable>)
表示如果具有實現 Serialzable 接口的對象,printAll 將打印 MyList
更准確地說,它意味着一個呼叫printAll
如果它傳遞一個只會編譯MyList
有一些通用的類型,它是或實現Serializable
。 在這種情況下,它將接受MyList<Serializable>
、 MyList<Integer>
等。
我對
super
有點問題。printAll(MyList<? super MyClass>)
表示printAll 將打印 MyList 如果它有 MyClass 的對象或任何擴展 MyClass 的類( MyClass的后代)
以super
為界的通配符是下界。 因此,我們可以說一個呼叫printAll
如果它傳遞一個只會編譯MyList
有一些通用的類型為MyClass
或一些超級型MyClass
。 因此,在這種情況下,它將接受MyList<MyParentClass>
MyList<MyClass>
,例如MyList<MyParentClass>
或MyList<MyParentClass>
MyList<Object>
。
所以,假設 MyClass 看起來像:
public class MyClass extends Thread implements ActionListener{ // whatever }
然后,printAll() 將打印如果
- 列表中有 MyClass 的對象
- 列表中有 Thread 或 ActionListener 的對象
你在正確的軌道上。 但是我認為說例如“如果列表中有MyClass
對象,它就會打印”是有問題的。 這聽起來像是在定義運行時行為 - 泛型都是關於編譯時檢查的。 例如,不能將MyList<MySubclass>
作為參數傳遞給MyList<MySubclass>
MyList<? super MyClass>
MyList<? super MyClass>
,即使它可能包含MyClass
實例,通過繼承。 我將其改寫為:
對printAll(MyList<? super MyClass>)
的調用只有在傳遞了以下參數時才會編譯:
MyList<MyClass>
MyList<Thread>
MyList<Runnable>
MyList<ActionListener>
MyList<EventListener>
MyList<Object>
MyList<? super X>
MyList<? super X>
其中X
是MyClass
、 Thread
、 Runnable
、 ActionListener
、 EventListener
或Object
。因此,在閱讀了該問題的許多答案后,這是我的理解:
? extends T
? extends T
表示任何擴展 T 的類。 因此,我們指的是 T 的孩子。因此, T 是上限。 繼承層次結構中最上層的類
? super T
? super T
指任何類/接口是super
T.因此我們指的是所有T. T的父母因此下界。 繼承層次結構中最底層的類
關閉,但我不會說“ T
孩子”或“ T
父母”,因為這些界限是包含性的——說“ T
或其子類型”和“ T
或其超類型”會更准確。
首先, T
或E
或K
或其他任何不是固定名稱。 它們只是類型變量,由您決定它們的名稱。 T
、 E
、 K
只是示例,但您可以將其稱為Foo
或其他名稱。
現在去你的第一個問題:因為通配符?
表示“任何和未知”類型,未指定的類型,在未指定的類型上聲明泛型類沒有任何意義。 當您不關心類型時,在方法的參數或變量中使用通配符很有用。
現在關於您的第二個問題:下限為您的通用方法提供了更大的靈活性。 extends
和super
都是相反的:
? extends T
? extends T
:未知類型,它是T
的子類型? super T
? super T
:未知類型,它是T
的超類型當您想要接受與 T 兼容的類型(以便 T 是該類型)時,后者會很有用。 可以在此處找到一個實際示例。
讓我們從頭開始。
嚴格來說,任何有效的 java 標識符都可以用作泛型類型參數——它只是一種特殊類型的變量:
public static final class MyGenericClass<MyGenericType> {
}
是完全有效的 Java。
接下來,您可以使用?
任何可以聲明的地方。 您可以在聲明變量時使用通配符,但不能在實例化它們時使用:
public static final class MyGenericClass {
private final Collection<? extends String> myThings;
public MyGenericClass(Collection<? extends String> myThings) {
this.myThings = myThings;
}
public void doStuff(final Collection<? extends String> myThings) {
}
}
再次全部有效,您不能這樣做:
final Collection<? extends String> myThings = new ArrayList<? extends String>();
當涉及到extends
與super
這被稱為協方差與逆變。 它確定允許提供的類型沿類層次結構的哪個方向行進:
final Collection<? extends Runnable> example1 = new ArrayList<Runnable>();
final Collection<? extends Runnable> example2 = new ArrayList<TimerTask>();
final Collection<? super Runnable> example3 = new ArrayList<Runnable>();
final Collection<? super Runnable> example4 = new ArrayList<Object>();
前兩個示例演示了extends
- 您可以從Collection
假設的最緊密的界限是Runnable
因為用戶可以傳遞在其繼承層次結構中具有Runnable
的任何東西的Collection
。
第二個兩個示例演示super
-最嚴格的約束,你可以從假設Collection
的Object
,因為我們允許任何處於繼承層次Runnable
。
對於第一個問題:你不能用?
作為類型參數。 以下不會編譯:
void <?> foo() {}
?
用於綁定到另一個泛型而不提供類型參數。 您可以為方法編寫:
void foo(List<?> e) {}
你也可以為類編寫:
public class Bar<E extends List<?>> { }
對於super
的使用:
public void printAll(MyList<? super MyClass>){
// code code code
}
這不會像你說的那樣打印列表“如果它有 MyClass 的對象”。 它可以擁有作為 MyClass 父類的子類的任何類的對象。 編譯器在編譯時並不知道列表中的對象是什么。
為了解決這個問題,請考慮一個帶有Number
類層次結構的簡單示例。 Float
和Integer
是Number
孩子。 你可以這樣寫你的方法:
public void printAll(List<? super Float>){
// code code code
}
然后您可以使用List<Number>
調用該方法:
List<Number> numbers = new ArrayList<>();
numbers.add(1); // actually only add an Integer
printAll(numbers); // compiles.
在這種情況下,這可能不會非常有用。 例如,當您想將Float
添加到集合而不希望它只是一個列表時,它會很有用,例如:
public void addFloat(List<? super Float> list){
list.add(2.5);
}
下限表示:您可以使用在 'super' 關鍵字之后提到的類,或其任何超類型。 這可能會變得棘手。 這些超類型可能有其他(完全不同的)類繼承自它們,如下例所示。
說,
List<? super Car> cars = new ArrayList <Vehicle>();
是否允許程序員編寫:
cars.add(new Helicopter()); //Helicopter is a kind of Vehicle
這顯然不應該被允許,它反映了使用下限的危險。
應該允許程序員將車輛添加到列表中,但不能添加任何車輛。 他必須投射它,讓 Java 知道他畢竟只是添加了一輛 Car Vehicle,就像這樣:
cars.add((Car) new Vehicle());
嗯,您對super
( printAll(MyList<? super MyClass>)
( printAll(MyList<? super MyClass>)
) 的聲明不清楚。 這意味着,假設 Myclass 擴展Object
是你可以printAll(MyList<MyClass>)
並且你可以printAll(MyList<Object>)
但沒有別的......這意味着printAll(MyList<Object>)
的泛型類型必須是一個超類(不是 MyClass 的子類)。 這和你說的不一樣。
至於 T、E、K、V 或 N,好吧,它們本身就是毫無意義的名稱。 你可以使用任何你想要的東西。 約定建議使用單字母大寫值,T 通常用於泛型方法,E 用於類......
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.