Java Language确定在“ T”,`之间?超级T`和`?扩展T`

示例

Java泛型有界通配符的语法,用来表示未知类型?:

  • ? extends T表示上限通配符。未知类型表示必须是T的子类型或类型T本身的类型。

  • ? super T表示下界通配符。未知类型表示必须是T的超类型或类型T本身的类型。

根据经验,您应该使用

  • ? extends T 如果您只需要“读取”访问权限(“输入”)

  • ? super T 如果您需要“写”访问权限(“输出”)

  • T 如果您同时需要(“修改”)

使用extends或super通常会更好,因为使用它会使您的代码更灵活(例如:允许使用子类型和超类型),如下所示。

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()); // 允许,因为苹果是水果
    fruits.add(new Banana()); // 允许,因为香蕉是一种水果
    fruitHelper.addApple(fruits); // 允许的, as "Fruit super Apple"
    fruitHelper.eatAll(fruits); // 允许的

    Collection<Banana> bananas = new ArrayList<>();
    bananas.add(new Banana()); // 允许的
    //fruitHelper.addApple(bananas); // 编译错误:只能包含香蕉!
    fruitHelper.eatAll(bananas); // 允许的, as all Bananas are Fruits

    Collection<Apple> apples = new ArrayList<>();
    fruitHelper.addApple(apples); // 允许的
    apples.add(new GrannySmith()); // 允许的, as this is an Apple
    fruitHelper.eatAll(apples); // 允许的, as all Apples are Fruits.
    
    Collection<GrannySmith> grannySmithApples = new ArrayList<>();
    fruitHelper.addApple(grannySmithApples); //编译错误:不允许。
                                   // GrannySmith不是Apple的超类型
    apples.add(new GrannySmith()); //仍然允许,GrannySmith是一个苹果
    fruitHelper.eatAll(grannySmithApples);//仍然允许,GrannySmith是一种水果

    Collection<Object> objects = new ArrayList<>();
    fruitHelper.addApple(objects); // 允许的, as Object super Apple
    objects.add(new Shoe()); // 不是水果
    objects.add(new IPhone()); // 不是水果
    //fruitHelper.eatAll(objects); // 编译错误:也可能包含Shoe!
}

选择right T,? super T或者? extends T是允许与子类型一起使用所必需的。然后,编译器可以确保类型安全。如果正确使用它们,则无需进行强制转换(这不是类型安全的,并且可能导致编程错误)。

如果不容易理解,请记住PECS规则:

P roducer使用“ Ë xtends”和Ç onsumer使用“ Ş UPER”。

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