在面向对象编程和类型系统中,协变(Covariance)和反协变(Contravariance)是处理类型继承和子类化关系的重要概念。这些概念主要用于理解和管理类型在泛型、方法返回类型、参数类型等场景中的行为。以下是对协变和反协变的详细解释。
协变(Covariance)
协变是指类型可以从子类型转换为父类型。换句话说,如果类型 B
是类型 A
的子类型,那么 B[]
也是 A[]
的子类型。协变通常适用于返回类型,即允许方法的返回类型是其声明的返回类型的子类型。
示例:数组的协变
在Java中,数组是协变的。这意味着你可以将一个子类数组赋值给一个父类数组的引用。
public class Main {
public static void main(String[] args) {
Integer[] integers = {1, 2, 3};
Number[] numbers = integers; // 数组是协变的
numbers[0] = 1.5; // 运行时会抛出 ArrayStoreException
}
}
尽管数组在Java中是协变的,但这种协变可能会导致运行时错误,如上例中的 ArrayStoreException
。
示例:泛型的协变(使用通配符)
在Java泛型中,可以使用通配符 ? extends T
来实现协变。
import java.util.List;
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
List<Integer> integerList = new ArrayList<>();
List<? extends Number> numberList = integerList; // 泛型协变
// 不能向 numberList 添加元素,因为编译器无法确定类型的安全性
// numberList.add(1); // 编译错误
Number num = numberList.get(0); // 读取是安全的
}
}
在这个示例中,List<? extends Number>
是 List<Integer>
的父类型,这实现了泛型的协变。
反协变(Contravariance)
反协变是指类型可以从父类型转换为子类型。换句话说,如果类型 A
是类型 B
的子类型,那么 A[]
可以被看作是 B[]
的子类型。反协变通常适用于方法的参数类型,即允许方法的参数类型是其声明的参数类型的父类型。
示例:泛型的反协变(使用通配符)
在Java泛型中,可以使用通配符 ? super T
来实现反协变。
import java.util.List;
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
List<Integer> integerList = new ArrayList<>();
List<? extends Number> numberList = integerList; // 泛型协变
// 不能向 numberList 添加元素,因为编译器无法确定类型的安全性
// numberList.add(1); // 编译错误
Number num = numberList.get(0); // 读取是安全的
}
}
在这个示例中,List<? super Integer>
是 List<Number>
的父类型,这实现了泛型的反协变。
协变和反协变的应用
- 方法返回类型的协变:子类方法可以返回比父类方法更具体的类型。
- 方法参数类型的反协变:子类方法可以接受比父类方法更通用的参数类型。
- 泛型的协变和反协变:使用通配符
? extends T
和? super T
来实现泛型类型的协变和反协变。
示例:方法返回类型的协变
class Animal {
public Animal getAnimal() {
return new Animal();
}
}
class Dog extends Animal {
@Override
public Dog getAnimal() {
return new Dog(); // 返回类型的协变
}
}
示例:方法参数类型的反协变
class Processor<T> {
public void process(T item) {
// 处理 item
}
}
class IntegerProcessor extends Processor<Number> {
@Override
public void process(Number item) {
// 处理 Number 类型的 item
}
}
在这个示例中,IntegerProcessor
类中的 process
方法接受 Number
类型的参数,而不是 Integer
类型的参数,这是方法参数类型的反协变。
总结
- 协变(Covariance):允许子类型转换为父类型,主要应用于返回类型。例如,
List<? extends T>
实现了协变。 - 反协变(Contravariance):允许父类型转换为子类型,主要应用于参数类型。例如,
List<? super T>
实现了反协变。
通过理解和应用协变和反协变,可以编写更加灵活和通用的代码,特别是在处理泛型和多态性时。