Java 协变数组类型

本文探讨了Java中数组的协变类型特性及其潜在问题,通过示例解释了为何Employee[]不能作为Person[]使用,以及这背后的运行时类型检查机制。

问题

  假设现在有 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 的缺陷。

Java 中,可使用 `List` 接口提供的 `toArray` 方法将 `List` 转换为数组。`toArray` 方法有两种重载形式: ### 1. `toArray()` 方法 此方法会返回一个包含 `List` 中所有元素的 `Object` 数组。以下是示例代码: ```java import java.util.ArrayList; import java.util.List; public class ListToArrayExample { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("Apple"); list.add("Banana"); list.add("Orange"); // 使用 toArray() 方法将 List 转换为 Object 数组 Object[] array = list.toArray(); // 遍历数组并输出元素 for (Object element : array) { System.out.println(element); } } } ``` ### 2. `toArray(T[] a)` 方法 该方法能返回一个按照正确顺序包含 `List` 中所有元素的数组,返回数组的运行时类型就是指定数组的运行时类型。若 `List` 能放入指定的数组,则返回放入此 `List` 元素的数组;否则,将根据指定数组的运行时类型和此 `List` 的大小分配一个新的数组 [^3]。示例代码如下: ```java import java.util.ArrayList; import java.util.List; public class ListToArrayGenericExample { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("Apple"); list.add("Banana"); list.add("Orange"); // 创建一个指定类型数组 String[] array = new String[list.size()]; // 使用 toArray(T[] a) 方法将 List 转换为指定类型数组 array = list.toArray(array); // 遍历数组并输出元素 for (String element : array) { System.out.println(element); } } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值