理解逆变、协变和不变:继承关系的类型转换
2024-02-14 01:31:24
在编程的世界里,类型转换是家常便饭。它允许我们操纵不同类型的对象,就像它们属于同一种类型一样。然而,这种转换的幕后机制会带来一些微妙的差别,这些差别可能会让我们感到困惑。逆变、协变和不变就是这些差别的术语。
逆变:子类到超类的转换
逆变是指从子类到超类的类型转换。当子类 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 对象。