PECS 原则
在使用限定通配符(bounded wildcard)时,需要遵守 PECS 原则。PECS 是 Producer-Extends, Consumer-Super 的缩写,是一种设计原则,用于指导使用泛型类型和限定通配符时的编程风格。
PECS 原则的核心思想是:Producer 使用 extends 通配符,Consumer 使用 super 通配符。
当泛型类型只用于产生(读取)元素时,即作为生产者时,使用 ? extends T 形式的限定通配符。这表示可以接受 T 或 T 的子类型。通过使用 extends,我们可以从集合中安全地获取元素,因为我们只关心元素的上界,而不关心具体的子类型。
当泛型类型只用于消费(写入)元素时,即作为消费者时,使用 ? super T 形式的限定通配符。这表示可以接受 T 或 T 的超类型。通过使用 super,我们可以向集合中安全地添加元素,因为元素一定是 T 或 T 的父类型。
通过遵循 PECS 原则,可以确保泛型在多态的使用中具有更灵活和安全的行为。下面是一些示例:
作为生产者使用 ? extends T:
public void process(List<? extends Animal> animals) {
for (Animal animal : animals) {
animal.eat();
}
}
在上述例子中,List<? extends Animal> 可以接受 Animal 或 Animal 的子类型的集合。我们只关心元素是 Animal 或其子类型,因此可以安全地读取元素。
c作为消费者使用 ? super T:
public void feed(List<? super Dog> dogs) {
dogs.add(new Dog());
dogs.add(new Puppy());
}
在上述例子中,List<? super Dog> 可以接受 Dog 或 Dog 的父类型的集合。我们可以添加 Dog 或其子类型的实例到集合中,因为集合的元素至少是 Dog。但如果使用 List<? extends Dog>,则无法添加元素,因为无法确定元素的类型。
通过遵循 PECS 原则,可以确保代码更加灵活、类型安全,并且在处理泛型集合时提供更好的扩展性和适应性。
T extends Animal 和 ? extends Animal 之间的差别
T extends Animal 是一个泛型声明,它将类型参数 T 限定为 Animal 或 Animal 的子类型。这意味着我们在使用这个泛型类型时,可以指定具体的类型参数,例如 List< Dog >、List< Cat >
而 ? extends Animal 是一个通配符表达式,用于限定通配符类型。它表示可以接受任何 Animal 或 Animal 的子类型的实例。通配符类型一般用于方法的参数或返回类型中,以表示方法对具体类型的灵活性。
在使用上,T extends Animal 允许在编译时具体指定类型参数,而 ? extends Animal 只能接受一种未知类型的 Animal 或 Animal 的子类型。这意味着 ? extends Animal 可能更加灵活,因为它可以接受更多可能的类型参数。
下面是一些具体的示例,来说明两者之间的差别:
// 泛型声明
class SomeClass<T extends Animal> {
// 使用具体的类型参数
private List<T> animals;
public SomeClass() {
animals = new ArrayList<>();
}
public void addAnimal(T animal) {
animals.add(animal);
}
public T getAnimal(int index) {
return animals.get(index);
}
}
// 通配符
void processAnimals(List<? extends Animal> animals) {
for (Animal animal : animals) {
animal.eat();
}
}
public static void main(String[] args) {
// 使用泛型声明
SomeClass<Dog> dogClass = new SomeClass<>();
dogClass.addAnimal(new Dog());
dogClass.addAnimal(new Puppy()); // 是 Dog 的子类型,可以用于 T extends Animal
Dog dog = dogClass.getAnimal(0);
// 使用通配符类型
List<Dog> dogs = new ArrayList<>();
dogs.add(new Dog());
dogs.add(new Puppy());
processAnimals(dogs); // 可以匹配 ? extends Animal
}
总结来说,T extends Animal 是一种具体的类型参数限定,而 ? extends Animal 是一种灵活的通配符类型限定。根据具体需求,你可以选择使用其中之一来实现不同的泛型行为。