数组的协变性

Bill Venners: Can you elaborate on the problem with Java's covariant arrays?
Martin Odersky: When Java first shipped, Bill Joy and James Gosling and the other members of the Java team thought that Java should have generics, only they didn't have the time to do a good job designing it in. So because there would be no generics in Java, at least initially, they felt that arrays had to be covariant. That means an array of String is a subtype of array of Object, for example. The reason for that was they wanted to be able to write, say, a “generic” sort method that took an array of Object and a comparator and that would sort this array of Object. And then let you pass an array of String to it. It turns out that this thing is type unsound in general. That's why you can get an array store exception in Java. And it actually also turns out that this very same thing blocks a decent implementation of generics for arrays. That's why arrays in Java generics don't work at all. You can't have an array of list of string, it's impossible. You're forced to do the ugly raw type, just an array of list, forever. So it was sort of like an original sin. They did something very quickly and thought it was a quick hack. But it actually ruined every design decision later on. So in order not to fall into the same trap again, we had to break off and say, now we will not be upwards compatible with Java, there are some things we want to do differently."
大意是说,java创始之初,人们想给它做泛型,但是没时间做,所以把数组设计成协变的了。但是,这是个后患无穷的设计。


ArrayList的内部实现是这个数组:
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer.
*/
private transient Object[] elementData;
在真正引入泛型之前,如果数组不能协变,这类以数组为基础的容器类恐怕就无法工作,或者难以使用了。

1、数组的协变性

数组的协变性(covariant)是指:

如果类Base是类Sub的基类,那么Base[]就是Sub[]的基类。

而泛型是不可变的(invariant),List<Base>不会是List<Sub>的基类,更不会是它的子类。


数组的协变性可能会导致一些错误,比如下面的代码:

public static void main(String[] args) { 
    Object[] array = new String[10]; 
    array[0] = 10; 
}

它是可以编译通过的,因为数组是协变的,Object[]类型的引用可以指向一个String[]类型的对象

但是运行的时候是会报出如下异常的:

Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer

但是对于泛型就不会出现这种情况了:

public static void main(String[] args) { 
    List< Object> list = new ArrayList< String>(); 
    list.add(10); 
}

这段代码连编译都不能通过。

2、数组的具体化。

数组是具体化的(reified),而泛型在运行时是被擦除的(erasure)。

数组是在运行时才去判断数组元素的类型约束,

而泛型正好相反,在运行时,泛型的类型信息是会被擦除的,只有编译的时候才会对类型进行强化。

所以上面的例子中,数组的方法会在运行时报出ArrayStoreException,而泛型根本无法通过编译。

 

3、泛型不是协变的

 

虽然将集合看作是数组的抽象会有所帮助,但是数组还有一些集合不具备的特殊性质。

Java 语言中的数组是协变的(covariant),也就是说,如果 Integer扩展了 Number(事实也是如此),那么不仅 Integer是 Number,而且 Integer[]也是 Number[],在要求 Number[]的地方完全可以传递或者赋予 Integer[]。(更正式地说,如果 Number是 Integer的超类型,那么 Number[]也是 Integer[]的超类型)。

您也许认为这一原理同样适用于泛型类型 —— List<Number>是 List<Integer>的超类型,那么可以在需要 List<Number>的地方传递 List<Integer>。不幸的是,情况并非如此。

不允许这样做有一个很充分的理由:

这样做将破坏要提供的类型安全泛型。

如果能够将 List<Integer>赋给 List<Number>。

那么下面的代码就允许将非 Integer的内容放入 List<Integer>

List<Integer> li = new ArrayList<Integer>();
List<Number> ln = li; // illegal
ln.add(new Float(3.1415));

因为 ln是 List<Number>,所以向其添加 Float似乎是完全合法的。但是如果 ln是 li的别名,那么这就破坏了蕴含在 li定义中的类型安全承诺 —— 它是一个整数列表,这就是泛型类型不能协变的原因。

转载于:https://my.oschina.net/swearyd7/blog/469792

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值