1.简介
我们可能希望将数组用作支持泛型的类或函数的一部分。由于Java处理泛型的方式,这可能很困难。
在本教程中,我们将了解对数组使用泛型的挑战。然后,我们将创建一个通用数组的示例。
我们还将看看Java API在哪里解决了类似的问题。
2.使用通用数组时的注意事项
数组与泛型之间的重要区别在于它们如何执行类型检查。具体来说,数组在运行时存储和检查类型信息。但是,泛型在编译时检查类型错误,并且在运行时没有类型信息。
Java的语法表明我们可能能够创建一个新的通用数组:
T[] elements = new T[size];
但是,如果尝试这样做,则会出现编译错误。
要了解原因,请考虑以下内容:
public <T> T[] getArray(int size) {
T[] genericArray = new T[size]; // suppose this is allowed
return genericArray;
}
当未绑定的通用类型T
解析为Object,
我们在运行时的方法将是:
public Object[] getArray(int size) {
Object[] genericArray = new Object[size];
return genericArray;
}
然后,如果我们调用方法并将结果存储在String
数组中:
String[] myArray = getArray(5);
该代码可以正常编译,但在运行时失败,并带有ClassCastException
。这是因为我们刚刚将Object[]
分配给String[]
引用。具体来说,编译器进行隐式强制转换将无法将Object[]
转换为我们所需的String[]
类型。
尽管我们不能直接初始化通用数组,但是如果调用代码提供了准确的信息类型,仍然有可能实现等效操作。
3.创建一个通用数组
对于我们的示例,让我们考虑一个有界堆栈数据结构MyStack
,其中容量固定为一定大小。另外,由于我们希望堆栈可以使用任何类型,因此合理的实现选择将是通用数组。
首先,让我们创建一个字段来存储堆栈元素,这是类型E
的通用数组:
private E[] elements;
其次,让我们添加一个构造函数:
public MyStack(Class<E> clazz, int capacity) {
elements = (E[]) Array.newInstance(clazz, capacity);
}
注意我们java.lang.reflect.Array#newInstance
初始化我们的通用数组,该数组需要两个参数。第一个参数指定新数组中对象的类型。第二个参数指定要为数组创建多少空间。 Array#newInstance
的结果为Object
类型,我们需要将其E[]
以创建我们的通用数组。
我们还应该注意命名类型参数clazz
而不是class,
的惯例,这是Java中的保留字。
4.考虑ArrayList
4.1。使用ArrayList
数组
通常,使用通用ArrayList
代替通用数组会更容易。让我们看看如何更改MyStack
以使用ArrayList
。
首先,让我们创建一个字段来存储元素:
private List<E> elements;
其次,在我们的堆栈构造函数中,我们可以使用初始容量ArrayList
elements = new ArrayList<>(capacity);
它使我们的类更加简单,因为我们不必使用反射。另外,在创建堆栈时,我们不需要传递类文字。最后,由于我们可以设置ArrayList
的初始容量,因此可以获得与数组相同的好处。
因此,仅在极少数情况下或与需要数组的外部库建立接口时,才需要构造泛型数组。
4.2。 ArrayList
实现
有趣的是, ArrayList
本身是使用通用数组实现的。让我们窥视ArrayList
看看如何。
首先,让我们看一下列表元素字段:
transient Object[] elementData;
注意ArrayList
使用Object
作为元素类型。因为直到运行时才知道我们的通用类型,所以Object
用作任何类型的超类。
ArrayList
中的几乎所有操作都可以使用此通用数组,因为除了一种方法toArray
之外,它们不需要向外界提供强类型数组。
5.从集合构建数组
5.1。 LinkedList示例
让我们看一下在Java Collections API中使用通用数组的情况,我们将在一个集合中构建一个新的数组。
首先,让我们使用类型参数String
LinkedList
并向其中添加项目:
List<String> items = new LinkedList();
items.add("first item");
items.add("second item");
其次,让我们构建刚刚添加的项目的数组:
String[] itemsAsArray = items.toArray(new String[0]);
要构建我们的数组, List
。 toArray
方法需要一个输入数组。它纯粹使用此数组来获取类型信息,以创建正确类型的返回数组。
在上面的示例中,我们使用new String[0]
作为输入数组来构建结果String
数组。
5.2。 LinkedList.toArray
实现
让我们LinkedList.toArray
,看看它如何在Java JDK中实现。
首先,让我们看一下方法签名:
public <T> T[] toArray(T[] a)
其次,让我们看看在需要时如何创建新数组:
a = (T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
请注意,它是如何利用Array#newInstance
来构建新数组的,就像前面的堆栈示例一样。另外,请注意如何使用参数a
Array#newInstance.
提供类型。最后,将Array#newInstance
的结果强制转换为T[]
创建通用数组。
六,结论
在本文中,我们首先研究了数组与泛型之间的区别,然后介绍了创建泛型数组的示例。然后,我们展示了使用ArrayList
可能比使用通用数组更容易。最后,我们还研究了Collections API中通用数组的使用。
0 评论