在 T super 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()); // 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”。
(生产者只有写访问权限,而消费者只有读访问权限)