You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 13:00:17