eag*_*arn 157 java arrays generics language-design covariance
来自Joshua Bloch的Effective Java,
协变只是意味着如果X是Y的子类型,那么X []也将是Y []的子类型.数组是协变的因为字符串是Object So的子类型
String[] is subtype of Object[]
不变量仅仅意味着X不是Y的子类型,
List<X> will not be subType of List<Y>.
Run Code Online (Sandbox Code Playgroud)我的问题是为什么决定在Java中使数组协变?还有其他SO帖子,例如为什么阵列不变,但列出协变?,但他们似乎专注于Scala,我无法遵循.
Pau*_*ora 147
通过维基百科:
早期版本的Java和C#不包括泛型(又称参数多态).
在这样的设置中,使数组不变可以排除有用的多态程序.例如,考虑编写一个函数来重新排列数组,或者使用
Object.equals元素上的方法测试两个数组是否相等的函数.实现不依赖于存储在数组中的元素的确切类型,因此应该可以编写适用于所有类型数组的单个函数.很容易实现类型的功能Run Code Online (Sandbox Code Playgroud)boolean equalArrays (Object[] a1, Object[] a2); void shuffleArray(Object[] a);但是,如果将数组类型视为不变量,则只能在完全类型的数组上调用这些函数
Object[].例如,人们无法改变一串字符串.因此,Java和C#都会协同处理数组类型.例如,在C#中
string[]是一个子类型object[],而在Java中String[]是一个子类型Object[].
这回答了问题:"为什么是数组协变?",或者更准确的说,"为什么是阵列由协变的时候?"
当引入仿制药时,由于Jon Skeet在这个答案中指出的原因,它们故意不会变成协变的:
不,a
List<Dog>不是List<Animal>.考虑你可以做什么List<Animal>- 你可以添加任何动物......包括一只猫.现在,你可以逻辑地将一只猫添加到一窝幼犬吗?绝对不.Run Code Online (Sandbox Code Playgroud)// 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);
Run Code Online (Sandbox Code Playgroud)
Kat*_*ona 28
原因是每个数组在运行时都知道它的元素类型,而泛型集合不是因为类型擦除.
例如:
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
Run Code Online (Sandbox Code Playgroud)
如果通用集合允许这样做:
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
Run Code Online (Sandbox Code Playgroud)
但是当有人试图访问列表时,这会导致问题:
String first = strings.get(0); // would cause ClassCastException, trying to assign 12 to String
Run Code Online (Sandbox Code Playgroud)
Rah*_*thi 21
可能是这个帮助: -
泛型不是协变的
Java语言中的数组是协变的 - 这意味着如果Integer扩展Number(它确实如此),那么不仅Integer也是Number,而且Integer []也是a Number[],你可以自由地传递或分配Integer[]需要a的地方Number[].(更正式地说,如果Number是Integer的超类型,则Number[]是超类型Integer[].)您可能认为泛型类型也是如此 - 这List<Number>是一种超类型List<Integer>,并且您可以传递a List<Integer>a a List<Number>expected.不幸的是,它没有那种方式.
事实证明它有一个很好的理由它不会那样工作:它会破坏应该提供的类型安全仿制药.想象一下,你可以指定List<Integer>一个List<Number>.然后,以下代码将允许您将不是Integer的内容放入List<Integer>:
List<Integer> li = new ArrayList<Integer>();
List<Number> ln = li; // illegal
ln.add(new Float(3.1415));
Run Code Online (Sandbox Code Playgroud)
因为ln是a List<Number>,所以添加Float似乎是完全合法的.但是如果ln有别名li,那么它将破坏li定义中隐含的类型安全承诺 - 它是一个整数列表,这就是泛型类型不能协变的原因.
| 归档时间: |
|
| 查看次数: |
30153 次 |
| 最近记录: |