首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么数组是协变的,而泛型是不变的?

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

Stack Overflow用户
提问于 2013-09-07 05:16:49
回答 9查看 39.1K关注 0票数 181

来自Joshua Bloch的Effective,

  1. 数组在两个重要方面与泛型类型不同。第一个数组是协变的。泛型是invariant.
  2. Covariant,简单地说,如果X是Y的子类型,那么X[]也将是Y[]的子类型。数组是协变的,因为字符串是对象的子类型,因此

String[] is subtype of Object[]

不变量简单地表示不管X是否是Y的子类型,

List将不是List的subType。

我的问题是为什么要让数组在Java中协变呢?还有其他的SO帖子,比如Why are Arrays invariant, but Lists covariant?,但它们似乎都集中在Scala上,我不能跟上。

EN

回答 9

Stack Overflow用户

回答已采纳

发布于 2013-09-07 05:30:50

通过wikipedia

和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使协方差(和逆方差)的表达成为可能,例如:

代码语言:javascript
复制
boolean equalLists(List<?> l1, List<?> l2);
void shuffleList(List<?> l);
票数 172
EN

Stack Overflow用户

发布于 2013-09-07 05:34:04

原因是每个数组在运行时都知道它的元素类型,而泛型收集由于类型擦除而不知道。

例如:

代码语言:javascript
复制
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

如果泛型集合允许这样做:

代码语言:javascript
复制
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

但当有人试图访问该列表时,这会导致问题:

代码语言:javascript
复制
String first = strings.get(0); // would cause ClassCastException, trying to assign 12 to String
票数 34
EN

Stack Overflow用户

发布于 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>

代码语言:javascript
复制
List<Integer> li = new ArrayList<Integer>();
List<Number> ln = li; // illegal
ln.add(new Float(3.1415));

因为ln是一个List<Number>,所以向它添加一个浮点数似乎完全合法。但是如果ln使用li作为别名,那么它就违背了li定义中隐含的类型安全承诺--它是一个整数列表,这就是为什么泛型类型不能协变的原因。

票数 24
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18666710

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档