前言

本章介绍一个简单的案例,实现数组拷贝,同时保留原数组的类型。

版本约定

如何编写一个通用的方法,实现数组的拷贝?

System.arraycopy方法提供了数组内容拷贝,下面进行第一次尝试。

  1. public static Object[] badCopyOf(Object[] original, int newLength) {
  2. Object[] newArray = new Object[newLength];
  3. System.arraycopy(original, 0, newArray, 0, Math.min(original.length, newLength));
  4. return newArray;
  5. }

这个方法在实际使用过程中,存在两个问题。

第一个问题:丢失原数组的类型,返回的是一个对象型数组(Object[])。

比如我传进去的是一个雇员数组(Employee[]),执行 badCopyOf 方法后,返回的是一个 Object[],且不能强转成雇员数组(Employee[])。

  1. Employee[] employees = new Employee[100];
  2. Employee[] employees2 = (Employee[]) badCopyOf(employees, 10);

运行程序,输出:

  1. Exception in thread "main" java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class [Ltest1.Employee; ([Ljava.lang.Object; is in module java.base of loader 'bootstrap'; [Ltest1.Employee; is in unnamed module of loader 'app')
  2. at test1.Test13.main(Test13.java:13)

上面的代码,运行时产生 ClassCastException 异常。Java 数组会记住每个元素的类型,即创建数组时 new 表达式中使用的元素类型,badCopyOf 方法使用new Object[newLength]创建的数组,所以数组中的元素都是 Object 类型,不能强转成 Employee[]。

将一个 Employee[] 数组临时地转换成 Object[] 数组,然后再把它转换回来是可以的,但一个从开始就是 Object[] 的数组却永远不能转换成 Employee[] 数组。

所以,需要在创建数组的时候,能够创建与原数组类型相同的新数组。java.lang.reflect 包中 Array 类提供了一个静态方法 newInstance,它能够构造指定类型的数组。在调用它时提供两个参数,一个是数组的元素类型,一个是数组的长度。

Object newArray = Array.newInstance(componentType, newLength);

  • 数组的元素类型:在确定传入的参数 original 是一个数组的情况下,通过 Class 类的 getComponentType 方法确定数组对应的类型。
  • 数组的长度:copyOf 方法已经传入新数组的长度。另外,可以使用 Array 类的静态 getLength 方法获得原数组的长度Array.getLength(original);

第二个问题:copyOf 方法是一个通用的方法,可以用来拷贝任意类型的数组,而不仅仅是对象数组。

比如,我想拷贝一个基本类型的数组int[] arr = {1, 2, 3, 4, 5};.

为了能够是想上述操作,应该将 copeOf 的参数声明为 Object 类型,而不要声明对象型数组(Object[])。整形数组 int[] 可以被转换成 Object,但不能转换成对象型数组(Object[])。

在解决了以上两个问题后,我们整理出的通用的拷贝数组方法如下所示。

  1. public static Object goodCopyOf(Object original, int newLength) {
  2. Class cls = original.getClass();
  3. if (!cls.isArray()) {
  4. return null;
  5. }
  6. Class componentType = cls.getComponentType();
  7. int length = Array.getLength(original);
  8. Object newArray = Array.newInstance(componentType, newLength);
  9. System.arraycopy(original, 0, newArray, 0, Math.min(length, newLength));
  10. return newArray;
  11. }

使用新的拷贝方法 goodCopyOf,再次执行上面的拷贝,就不会报错了。

  1. public static void main(String[] args) {
  2. Employee[] employees = new Employee[100];
  3. Employee[] employees2 = (Employee[]) goodCopyOf(employees, 10);
  4. }