Bootstrap

java~PECS原则

PECS(Producer Extends, Consumer Super)是Java泛型编程中的一个重要原则,用于指导如何使用通配符以增强代码的灵活性和可重用性。PECS原则可以分为两个部分:

  1. Producer Extends

    • 当你需要从某个集合中读取数据时,应该使用? extends T。这表示该集合可以是T或其子类的类型。
    • 适用于生产者(提供数据)的场景。
  2. Consumer Super

    • 当你需要向某个集合中写入数据时,应该使用? super T。这表示该集合可以是T或其父类的类型。
    • 适用于消费者(接收数据)的场景。

遵循PECS原则的好处

  • 类型安全:使用PECS原则可以确保在编译时进行类型检查,从而减少运行时错误。
  • 代码灵活性:允许更广泛的类型使用,使得代码更加灵活和可重用。
  • 简化复杂性:通过明确区分生产者和消费者的行为,可以使代码的意图更加清晰。

Java示例

java.util.Collections.copy方法使用了pecs原则

下面是一个示例,展示如何遵循PECS原则。我们将创建一个简单的类来演示生产者和消费者的使用。

示例代码

import java.util.ArrayList;
import java.util.List;

// 定义一个动物类及其子类
class Animal {
    public void makeSound() {
        System.out.println("Some sound");
    }
}

class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Woof");
    }
}

class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Meow");
    }
}

// 使用PECS原则的生产者方法
public class PECSExample {

    // 生产者方法:接受一个包含Animal或其子类的列表
    public static void printSounds(List<? extends Animal> animals) {
        for (Animal animal : animals) {
            animal.makeSound(); // 调用每个动物的发声方法
        }
    }

    // 消费者方法:接受一个包含Animal或其父类的列表
    public static void addDog(List<? super Dog> dogs) {
        dogs.add(new Dog()); // 可以添加Dog对象
    }

    public static void main(String[] args) {
        List<Dog> dogList = new ArrayList<>();
        dogList.add(new Dog());

        List<Cat> catList = new ArrayList<>();
        catList.add(new Cat());

        // 使用生产者方法
        System.out.println("Animal Sounds:");
        printSounds(dogList); // 输出:Woof
        printSounds(catList);  // 输出:Meow

        // 使用消费者方法
        List<Animal> animalList = new ArrayList<>();
        addDog(animalList); // 添加Dog到Animal列表
        for (Animal animal : animalList) {
            animal.makeSound(); // 输出:Woof
        }
    }
}

代码分析

  1. 动物类

    • Animal是一个基类,DogCat是其子类,分别实现了makeSound()方法。
  2. 生产者方法

    • printSounds(List<? extends Animal> animals)方法接受一个Animal或其子类的列表,并打印每个动物的声音。
  3. 消费者方法

    • addDog(List<? super Dog> dogs)方法接受一个Dog或其父类的列表,并向其中添加Dog对象。
  4. 主方法

    • 创建DogCat的列表,并调用生产者方法打印它们的声音。
    • 创建一个Animal类型的列表并使用消费者方法添加Dog对象。

SOLID中的里氏替换原则

// 入参是RedDog或者是它的父类
int sumInt(List<? super RedDog> list) {
		list.add(new RedDog("d1", true));
		list.forEach(o -> {
			((RedDog) o).makeSound();// 直接使用RedDog对象,符合里氏替换的原则,子类可以替换父类
		});
		return 0;
}

// 方法内部为父类集合赋值,在外面使用时,暴露出父类(子类公共字段和方法)来访问
List<? extends Dog> getDogs() {
		List<Dog> list = new ArrayList<>(); // 返回的集合中,有多种Dog的子类,在调用方使用时,是以dog的方式对外暴露的
		list.add(new RedDog("d1", true));
		list.add(new RedDog("d2", true));
		list.add(new BlackDog("black1", false));
		return list;
	}

总结

通过遵循PECS原则,我们能够更好地管理泛型类型的使用,提高代码的灵活性和可读性,同时保持类型安全。这种方式使得开发者在处理复杂数据结构时能够更有效地组织代码。

参考:https://zhuanlan.zhihu.com/p/51154783

;