小编典典

为什么数组是协变的,而泛型是不变的?

all

来自 Joshua Bloch 的 Effective Java,

  1. 数组在两个重要方面不同于泛型类型。第一个数组是协变的。泛型是不变的。
  2. 协变只是意味着如果 X 是 Y 的子类型,那么 X[] 也将是 Y[] 的子类型。数组是协变的,因为字符串是 Object 的子类型,所以

String[] is subtype of Object[]

不变只是意味着不管 X 是否是 Y 的子类型,

     List<X> will not be subType of List<Y>.

我的问题是为什么决定在 Java 中使数组协变?还有其他 SO 帖子,例如Why are Arrays invariant, but Lists
covariant?
,但他们似乎专注于 Scala,我无法遵循。


阅读 69

收藏
2022-07-17

共1个答案

小编典典

通过维基百科

Java 和 C# 的早期版本不包括泛型(也称为参数多态)。

在这种情况下,使数组保持不变会排除有用的多态程序。例如,考虑编写一个函数来对数组进行洗牌,或者编写一个使用Object.equals元素上的方法测试两个数组是否相等的函数。该实现不依赖于存储在数组中的元素的确切类型,因此应该可以编写一个适用于所有类型数组的函数。很容易实现类型的功能

boolean equalArrays (Object[] a1, Object[] a2);
void shuffleArray(Object[] a);

但是,如果数组类型被视为不变量,则只能在类型为 的数组上调用这些函数Object[]。例如,不能对字符串数组进行洗牌。

因此,Java 和 C# 都以协变方式处理数组类型。例如,在 C#string[]中是 的子类型object[],而在
JavaString[]中是 的子类型Object[]

这回答了“为什么数组是协变的?”这个问题,或者更准确地说,“为什么 当时 数组是协变 ?”

当引入泛型时,由于 Jon
Skeet
在这个答案中指出的原因,故意不使它们成为协变的:

不, aList<Dog>不是 a List<Animal>。考虑一下你可以用 a 做什么List<Animal>-
你可以在其中添加任何动物......包括一只猫。现在,你能合乎逻辑地将一只猫添加到一窝小狗中吗?绝对不。

// Illegal code - because otherwise life would be Bad
List<Dog> dogs = new List<Dog>();
List<Animal> animals = dogs; // Awooga awooga
animals.add(new Cat());
Dog dog = dogs.get(0); // This should be safe, right?

突然你有一只 非常 困惑的猫。

维基百科文章中描述的使数组协变的最初动机不适用于泛型,因为通配符使协变(和逆变)的表达成为可能,例如:

boolean equalLists(List<?> l1, List<?> l2);
void shuffleList(List<?> l);
2022-07-17