在 T super T 和 T 之間做出決定

Java 泛型有界萬用字元的語法,由 ? 表示未知型別:

  • ? extends T 代表一個上限有界的萬用字元。未知型別表示必須是 T 的子型別或型別 T 本身的型別。

  • ? super T 代表一個下限的萬用字元。未知型別表示必須是 T 的超型別或型別 T 本身的型別。

根據經驗,你應該使用

  • ? extends T 如果你只需要讀取訪問許可權(輸入
  • ? super T 如果你需要寫入訪問許可權(輸出
  • T 如果你需要兩者(修改

使用 extendssuper 通常*更好,*因為它使你的程式碼更靈活(如:允許使用子型別和超型別),如下所示。

class Shoe {}
class IPhone {}
interface Fruit {}
class Apple implements Fruit {}
class Banana implements Fruit {}
class GrannySmith extends Apple {}

   public class FruitHelper {

        public void eatAll(Collection<? extends Fruit> fruits) {}

        public void addApple(Collection<? super Apple> apples) {}
}

編譯器現在能夠檢測到某些不良用法:

 public class GenericsTest {
      public static void main(String[] args){
  FruitHelper fruitHelper = new FruitHelper() ;
    List<Fruit> fruits = new ArrayList<Fruit>();
    fruits.add(new Apple()); // Allowed, as Apple is a Fruit
    fruits.add(new Banana()); // Allowed, as Banana is a Fruit
    fruitHelper.addApple(fruits); // Allowed, as "Fruit super Apple"
    fruitHelper.eatAll(fruits); // Allowed

    Collection<Banana> bananas = new ArrayList<>();
    bananas.add(new Banana()); // Allowed
    //fruitHelper.addApple(bananas); // Compile error: may only contain Bananas!
    fruitHelper.eatAll(bananas); // Allowed, as all Bananas are Fruits

    Collection<Apple> apples = new ArrayList<>();
    fruitHelper.addApple(apples); // Allowed
    apples.add(new GrannySmith()); // Allowed, as this is an Apple
    fruitHelper.eatAll(apples); // Allowed, as all Apples are Fruits.
    
    Collection<GrannySmith> grannySmithApples = new ArrayList<>();
    fruitHelper.addApple(grannySmithApples); //Compile error: Not allowed.
                                   // GrannySmith is not a supertype of Apple
    apples.add(new GrannySmith()); //Still allowed, GrannySmith is an Apple
    fruitHelper.eatAll(grannySmithApples);//Still allowed, GrannySmith is a Fruit

    Collection<Object> objects = new ArrayList<>();
    fruitHelper.addApple(objects); // Allowed, as Object super Apple
    objects.add(new Shoe()); // Not a fruit
    objects.add(new IPhone()); // Not a fruit
    //fruitHelper.eatAll(objects); // Compile error: may contain a Shoe, too!
}

選擇正確的 T? super T? extends T 是*必要的,*以允許使用亞型。然後編譯器可以確保型別安全; 如果你正確使用它們,你不需要強制轉換(這不是型別安全的,可能會導致程式設計錯誤)。

如果不容易理解,請記住 PECS 規則:

P roducer 使用“ E xtends”, C onsumer 使用“ S uper”。

(生產者只有寫訪問許可權,而消費者只有讀訪問許可權)