返回

理解逆变、协变和不变:继承关系的类型转换

Android

在编程的世界里,类型转换是家常便饭。它允许我们操纵不同类型的对象,就像它们属于同一种类型一样。然而,这种转换的幕后机制会带来一些微妙的差别,这些差别可能会让我们感到困惑。逆变、协变和不变就是这些差别的术语。

逆变:子类到超类的转换

逆变是指从子类到超类的类型转换。当子类 A 是超类 B 的子类时,我们说 A 是 B 的逆变。换句话说,如果我们可以安全地将 A 对象转换为 B 对象,那么 A 就是 B 的逆变。

协变:超类到子类的转换

协变是指从超类到子类的类型转换。当超类 B 是子类 A 的超类时,我们说 B 是 A 的协变。换句话说,如果我们可以安全地将 B 对象转换为 A 对象,那么 B 就是 A 的协变。

不变:类型之间的转换

不变是指没有类型转换发生的情况。当两个类型 A 和 B 没有继承关系时,我们说 A 和 B 是不变的。换句话说,我们不能安全地将 A 对象转换为 B 对象,反之亦然。

理解逆变和协变的含义

了解逆变和协变对于编写健壮且可维护的代码非常重要。例如,考虑以下示例:

class Animal { }
class Dog extends Animal { }

List<Animal> animals = new ArrayList<>();
animals.add(new Dog()); // 协变:从 Dog 到 Animal 的转换是安全的

在这个示例中,我们可以将 Dog 对象添加到 Animal 列表中,因为 Dog 是 Animal 的子类。这是协变的一个例子,因为超类(Animal)可以安全地转换为子类(Dog)。

实际应用:不可变集合

逆变和协变的另一个重要应用是不可变集合。在 Java 中,集合可以声明为不可变,这意味着一旦创建集合,就不能再添加或删除元素。这可以通过使用 Collections.unmodifiableList() 等方法来实现。

不可变集合的好处是它们线程安全且易于推理。然而,在某些情况下,我们可能需要从不可变集合中获取子类对象。在这种情况下,逆变就很重要了。例如:

List<Animal> unmodifiableAnimals = Collections.unmodifiableList(animals);
Animal animal = unmodifiableAnimals.get(0); // 逆变:从 Animal 到 Dog 的转换是安全的

在这个示例中,我们从不可变的 Animal 列表中获取了一个 Animal 对象。然而,我们知道列表中实际包含 Dog 对象。逆变允许我们安全地将 Animal 对象转换为 Dog 对象。