如何让Java泛型列表同时支持类及其接口的OnboardingStep(逆变)
解决泛型兼容问题:让入职流程同时支持通用与专属步骤
Java的泛型默认是不变的——哪怕Developer是Employee的子类,OnboardingStep<Employee>和OnboardingStep<Developer>之间也没有继承关系,所以你无法把GeneralOnboardingStep(实现OnboardingStep<Employee>)放入要求OnboardingStep<Developer>的列表中。
解决方案
利用泛型的逆变特性,结合PECS原则(消费者用Super),调整抽象类中列表的泛型类型:
修改OnboardingProcess中的列表和构造函数参数类型为List<OnboardingStep<? super T>>,这样列表就能同时容纳:
- 针对
T本身的步骤(比如DeveloperOnboardingStep) - 针对
T的超类型的步骤(比如GeneralOnboardingStep,因为Employee是Developer的超类型)
修改后的完整代码
abstract class OnboardingProcess<T extends Employee> { // 修改泛型类型为 ? super T private final List<OnboardingStep<? super T>> onboardingSteps; protected OnboardingProcess(List<OnboardingStep<? super T>> onboardingSteps) { this.onboardingSteps = onboardingSteps; } public void perform(T newEmployee) { for (var onboardingStep : onboardingSteps) { onboardingStep.perform(newEmployee); } } } class DeveloperOnboardingProcess extends OnboardingProcess<Developer> { DeveloperOnboardingProcess() { // 现在可以正常传入两种步骤了 super(List.of( new GeneralOnboardingStep(), new DeveloperOnboardingStep() )); } }
原理说明
当我们调用onboardingStep.perform(newEmployee)时,是把T类型的对象传入perform方法,属于消费T的场景。根据PECS原则,这种情况下应该用? super T来声明泛型:
OnboardingStep<? super T>表示这个步骤的perform方法可以接受T或者T的任意超类型参数- 由于
Developer是Employee的子类,把Developer对象传给perform(Employee)方法是完全安全的(子类对象可以赋值给超类型变量)
内容的提问来源于stack exchange,提问作者Elias Schoof
相关产品推荐
相关产品推荐

