簡體   English   中英

了解上界和下界 ? 在 Java 泛型中

[英]Understanding upper and lower bounds on ? in Java Generics

我真的很難理解通配符參數。 我有幾個問題。

  1. ? 作為類型參數只能在方法中使用。 例如: printAll(MyList<? extends Serializable>)我不能用? 作為類型參數。

  2. 我了解上限? . printAll(MyList<? extends Serializable>)手段:“ printAll將打印MyList如果它具有實施該對象Serialzable接口”。
    我對super有點問題。 printAll(MyList<? super MyClass>)手段:“ printAll將打印MyList如果它具有的對象MyClass或延伸的任何類MyClass (的后代MyClass )”。

糾正我哪里出錯了。

簡而言之,只有TEKVN可以用作定義泛型類的類型參數。 ? 只能在方法中使用


更新 1:

 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.有對象ThreadActionListenerList


更新 2:

因此,在閱讀了該問題的許多答案后,這是我的理解:

  1. ? extends T ? extends T表示任何擴展T 因此,我們指的T孩子 因此, T是上限。 繼承層次結構中最上層的類

  2. ? super T ? super T任何類/接口是superT 因此,我們指的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() 將打印如果

  1. 列表中有 MyClass 的對象
  2. 列表中有 Thread 或 ActionListener 的對象

你在正確的軌道上。 但是我認為說例如“如果列表中有MyClass對象,它就會打印”是有問題的。 這聽起來像是在定義運行時行為 - 泛型都是關於編譯時檢查的。 例如,不能將MyList<MySubclass>作為參數傳遞給MyList<MySubclass> MyList<? super MyClass> MyList<? super MyClass> ,即使它可能包含MyClass實例,通過繼承。 我將其改寫為:

printAll(MyList<? super MyClass>)的調用只有在傳遞了以下參數時才會編譯:

  1. MyList<MyClass>
  2. MyList<Thread>
  3. MyList<Runnable>
  4. MyList<ActionListener>
  5. MyList<EventListener>
  6. MyList<Object>
  7. MyList<? super X> MyList<? super X>其中XMyClassThreadRunnableActionListenerEventListenerObject

因此,在閱讀了該問題的許多答案后,這是我的理解:

? extends T ? extends T表示任何擴展 T 的類 因此,我們指的是 T 的孩子。因此, T 是上限。 繼承層次結構中最上層的類

? super T ? super T指任何類/接口是super T.因此我們指的是所有T. T的父母因此下界。 繼承層次結構中最底層的類

關閉,但我不會說“ T孩子”或“ T父母”,因為這些界限是包含性的——說“ T或其子類型”和“ T或其超類型”會更准確。

首先, TEK或其他任何不是固定名稱。 它們只是類型變量,由您決定它們的名稱。 TEK只是示例,但您可以將其稱為Foo或其他名稱。

現在去你的第一個問題:因為通配符? 表示“任何和未知”類型,未指定的類型,在未指定的類型上聲明泛型類沒有任何意義。 當您不關心類型時,在方法的參數或變量中使用通配符很有用。

現在關於您的第二個問題:下限為您的通用方法提供了更大的靈活性。 extendssuper都是相反的:

  • ? 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>();

當涉及到extendssuper這被稱為協方差與逆變。 它確定允許提供的類型沿類層次結構的哪個方向行進:

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 -最嚴格的約束,你可以從假設CollectionObject ,因為我們允許任何處於繼承層次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類層次結構的簡單示例。 FloatIntegerNumber孩子。 你可以這樣寫你的方法:

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.

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