逆变、协变、不变

119 阅读1分钟

类型擦除

在 Java 中,泛型类型参数只在编译时存在,而在运行时会变为 object,类型擦除的原因是为了保持 Java 的向后兼容性。通过类型擦除,泛型类型的对象可以与没有使用泛型的代码兼容,并且可以在没有泛型支持的旧版本的 Java 虚拟机上运行。

逆变、协变、不变

  • 不变:如果一个泛型类型参数在继承关系中保持不变,那么它被称为不变的。例如,List<String> 是不变的,因为 String 类型在继承关系中不会变化。
  • 协变:如果一个泛型类型参数在继承关系中可以被更具体的类型替换,那么它被称为协变的。例如,List<? extends Number> 是协变的,因为 Number 类型可以被其子类(如Float)型替换。
  • 逆变:如果一个泛型类型参数在继承关系中可以被更通用的类型替换,那么它被称为逆变的。例如,<? super Number> 是逆变的,因为Number类型可以被其父类型(如Object)替换。

协变和逆变只适用于泛型类型参数的引用类型,而不适用于基本类型。

// 这段代码编译会报错,因为类型擦除的问题,泛型是不变的
ArrayList<Number> list = new ArrayList<Integer>();

// 数组是协变的,原因是每个数组在运行时都知道其元素类型,而泛型集合不知道类型擦除。
Number[] numbers = new Integer[3];

PECS原则

  • 要从泛型类取数据时,用extends;
  • 要往泛型类写数据时,用super;
  • 既要取又要写,就不用通配符;