在 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”。

(生产者只有写访问权限,而消费者只有读访问权限)