通过维基百科:
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);