拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 用Java创建泛型通用数组

用Java创建泛型通用数组

白鹭 - 2021-11-24 478 0 0

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]);

要构建我们的数组, ListtoArray方法需要一个输入数组。它纯粹使用此数组来获取类型信息,以创建正确类型的返回数组。

在上面的示例中,我们使用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 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *