# 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. 理解到数组的协变特性是一个历史遗留问题,在新代码中应当谨慎使用