Java 协变数组类型

问题

  假设现在有 Person 类和 Employee 类,假设 Employee IS-A Person,那么,这是不是意味着数组 Employee[ ] IS-A Person[ ] 呢?换句话说,如果一个例程接受 Person[ ] 作为参数,我们能不能把 Employee[ ] 作为作为参数来传递呢?

思考

  乍一看,似乎 Employee[ ] 就应该和 Person[ ] 类型兼容。举个例子,假设除了 Employee 外,还有 Student IS-A Person,并假设 Employee[ ] 和 Person[ ] 是类型兼容的,此时考虑如下语句:

// 以下代码正常运行
Person[] arrayP1 = new Person[5];
arrayP1[0] = new Employee();
arrayP1[1] = new Student();

// 既然 Employee[] IS-A Person[]
// 那么意味着可将new Person[5]换为new Employee[5]
Person[] array = new Employee[5];
array[0] = new Employee();
array[1] = new Student();// ArrayStoreException

  array[0] 成功引用一个 Employee,array[1] 本来应该引用一个 Employee,却引用了一个 Student,可是 Student IS-NOT-A Employee,这样就造成了类型混乱,又因为不存在类型转换,Java 虚拟机不能抛出 ClassCastException 异常

Java 协变数组类型

  避免上述问题最容易的方法是设定这些数组不是类型兼容的,可是,在 Java 中数组却是类型兼容的,这叫做协变数组类型在 Java 中,每个数组都明了它所允许存储的对象类型,并且会在运行时做类型检查(这也是为什么不能创建泛型数组的原因,数组创建时必须知道确切类型)如果像上面一样将一个不兼容的类型插入数组中,那么虚拟机将抛出一个 ArrayStoreException 异常。
  数组记得它内部元素的具体类型,并且会在运行时做类型检查。这就是当初敢于把数组设计成协变的原因,这也是为什么容器 Collection 不能设计成协变的原因(Collection 不做运行时类型检查)。

一点历史

  因为 JDK 1.5 之前还没有泛型,但很多代码迫切需要泛型来解决问题,就只能使用协变数组解决。这是对早期没有泛型的妥协,《Effective Java》中明确地指出,Java 允许数组协变是 Java 的缺陷。

posted @ 2022-01-23 00:02  Acx7  阅读(65)  评论(0编辑  收藏  举报