来自Joshua Bloch的Effective,
String[] is subtype of Object[]
不变量简单地表示不管X是否是Y的子类型,
List将不是List的subType。
我的问题是为什么要让数组在Java中协变呢?还有其他的SO帖子,比如Why are Arrays invariant, but Lists covariant?,但它们似乎都集中在Scala上,我不能跟上。
发布于 2013-09-07 05:30:50
和C#的早期版本不包括泛型(也称为参数多态性)。
在这种情况下,使数组不变会排除有用的多态程序。例如,可以考虑编写一个对数组进行随机排列的函数,或者编写一个使用元素上的Object.equals
方法测试两个数组是否相等的函数。实现并不依赖于存储在数组中的元素的确切类型,因此应该可以编写一个适用于所有类型的数组的函数。类型的函数很容易实现。
boolean equalArrays (Object[] a1,Object[] a2);void shuffleArray(Object[] a);
但是,如果将数组类型视为不变类型,则只能在Object[]
类型的数组上调用这些函数。例如,人们不能对字符串数组进行混洗。
因此,Java和C#都以协变的方式对待数组类型。例如,在C#中,string[]
是object[]
的子类型,而String[]
是Object[]
的子类型。
这回答了“为什么数组是协变的?”这个问题,或者更准确地说,“为什么当时数组是协变的?”
在引入泛型时,出于this answer by Jon Skeet中指出的原因,故意不让它们成为协变
不,
List<Dog>
不是List<Animal>
。考虑一下你可以用List<Animal>
做什么--你可以向它添加任何动物……包括一只猫。现在,你能从逻辑上把一只猫加到一窝小狗中吗?绝对不是。
//非法代码-因为否则生活将是糟糕的列出狗=新列表();列出动物=狗;// Awooga awooga animals.add(新猫());狗= dogs.get(0);//这应该是安全的,对吧?
突然,你有了一只非常困惑的猫。
维基百科文章中描述的使数组协变的最初动机不适用于泛型,因为wildcards使协方差(和逆方差)的表达成为可能,例如:
boolean equalLists(List<?> l1, List<?> l2);
void shuffleList(List<?> l);
发布于 2013-09-07 05:34:04
原因是每个数组在运行时都知道它的元素类型,而泛型收集由于类型擦除而不知道。
例如:
String[] strings = new String[2];
Object[] objects = strings; // valid, String[] is Object[]
objects[0] = 12; // error, would cause java.lang.ArrayStoreException: java.lang.Integer during runtime
如果泛型集合允许这样做:
List<String> strings = new ArrayList<String>();
List<Object> objects = strings; // let's say it is valid
objects.add(12); // invalid, Integer should not be put into List<String> but there is no information during runtime to catch this
但当有人试图访问该列表时,这会导致问题:
String first = strings.get(0); // would cause ClassCastException, trying to assign 12 to String
发布于 2013-09-07 05:22:00
可能是this帮助:-
泛型不是协变的
Java语言中的数组是协变的--这意味着如果Integer扩展了Number (确实如此),那么Integer[]不仅是Integer也是Number,而且Number[]
也是Integer,您可以自由地在调用Integer[]
的地方传递或赋值Number[]
。(更正式地说,如果Number是Integer[]
的超类型,那么Number[]
就是Integer的超类型。)您可能认为泛型类型也是如此-- List<Number>
是List<Integer>
的超类型,您可以在需要List<Integer>
的地方传递List<Number>
。不幸的是,它不是这样工作的。
事实证明,它不能以这种方式工作有一个很好的理由:它会破坏泛型应该提供的类型安全。想象一下,您可以为List<Number>
分配一个List<Integer>
。然后,下面的代码将允许您将不是整数的内容放入List<Integer>
List<Integer> li = new ArrayList<Integer>();
List<Number> ln = li; // illegal
ln.add(new Float(3.1415));
因为ln是一个List<Number>
,所以向它添加一个浮点数似乎完全合法。但是如果ln使用li
作为别名,那么它就违背了li定义中隐含的类型安全承诺--它是一个整数列表,这就是为什么泛型类型不能协变的原因。
https://stackoverflow.com/questions/18666710
复制相似问题