前言
本章介绍一个简单的案例,实现数组拷贝,同时保留原数组的类型。
版本约定
- JDK Version:11.0.12
- Java SE API Documentation:https://docs.oracle.com/en/java/javase/11/docs/api/index.html
正文
如何编写一个通用的方法,实现数组的拷贝?
System.arraycopy
方法提供了数组内容拷贝,下面进行第一次尝试。
public static Object[] badCopyOf(Object[] original, int newLength) {
Object[] newArray = new Object[newLength];
System.arraycopy(original, 0, newArray, 0, Math.min(original.length, newLength));
return newArray;
}
这个方法在实际使用过程中,存在两个问题。
第一个问题:丢失原数组的类型,返回的是一个对象型数组(Object[])。
比如我传进去的是一个雇员数组(Employee[]),执行 badCopyOf 方法后,返回的是一个 Object[],且不能强转成雇员数组(Employee[])。
Employee[] employees = new Employee[100];
Employee[] employees2 = (Employee[]) badCopyOf(employees, 10);
运行程序,输出:
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')
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[])。
在解决了以上两个问题后,我们整理出的通用的拷贝数组方法如下所示。
public static Object goodCopyOf(Object original, int newLength) {
Class cls = original.getClass();
if (!cls.isArray()) {
return null;
}
Class componentType = cls.getComponentType();
int length = Array.getLength(original);
Object newArray = Array.newInstance(componentType, newLength);
System.arraycopy(original, 0, newArray, 0, Math.min(length, newLength));
return newArray;
}
使用新的拷贝方法 goodCopyOf,再次执行上面的拷贝,就不会报错了。
public static void main(String[] args) {
Employee[] employees = new Employee[100];
Employee[] employees2 = (Employee[]) goodCopyOf(employees, 10);
}