# Java中的协变性(Covariance)详解 ## 基本概念 协变(covariant)定义:如果类型Son是类型Father的子类型,那么复合类型Son[]也是Father[]的子类型。 ### 数组与List的协变性比较 - **数组是协变的**:Array[父类]是Array[子类]的父类型 - **List不是协变的**:List<父类>不是List<子类>的父类型 ## 设计原因 Java的这种设计主要基于历史原因: 1. **早期设计决策** - Java 1.0和1.1时期没有泛型 - 数组作为主要集合类型,设计成协变提供了更大灵活性 2. **向后兼容性** - Java 5(2004年)引入泛型时 - 为保持兼容性,保留了数组的协变特性 ## 协变的优缺点 ### 优点:提供灵活性 ```java // 数组协变示例 - 合法代码 class Animal {} class Dog extends Animal {} class AnimalShelter { public Animal[] getAnimals() { return new Animal[10]; } } class DogShelter extends AnimalShelter { @Override public Dog[] getAnimals() { // 这里是合法的 return new Dog[10]; } } ``` ### 缺点:类型安全问题 ```java // 数组协变的安全隐患 Number[] numbers = new Integer[10]; // 编译通过 numbers[0] = 3.14; // 运行时抛出ArrayStoreException // List不允许协变 - 编译期就能发现问题 List<Number> numberList = new ArrayList<Integer>(); // 编译错误 ``` ## 泛型擦除的影响 `List<T>`在运行时会发生类型擦除: - 数组`T[]`在运行时保留元素类型信息 - `List<T>`在运行时不保留泛型类型信息 ## 最佳实践 1. 优先使用List而不是数组,因为List提供了更好的类型安全性 2. 如果需要协变性,考虑使用通配符(例如:`List<? extends Animal>`) 3. 理解到数组的协变特性是一个历史遗留问题,在新代码中应当谨慎使用