当前位置: 首页 > 面试题库 >

Java如何创建通用数组?

樊运乾
2023-03-14
问题内容

我不理解泛型和数组之间的联系。

我可以使用通用类型创建数组引用:

private E[] elements; //GOOD

但是不能创建具有通用类型的数组对象:

elements = new E[10]; //ERROR

但它有效:

elements = (E[]) new Object[10]; //GOOD

问题答案:

你不应该混淆数组和泛型。他们在一起不好。数组和泛型类型执行类型检查的方式有所不同。我们说数组是经过整形的,而泛型则不是。结果,你会看到这些差异适用于数组和泛型。

数组是协变的,泛型不是:
那意味着什么?你现在必须已经知道以下分配有效:

Object[] arr = new String[10];

基本上,Object[]是的超类型String[],因为Object是的超类型String。对于泛型而言并非如此。因此,以下声明无效,并且不会编译:

List<Object> list = new ArrayList<String>(); // Will not compile.

原因是,泛型是不变的。

强制类型检查:

Java中引入了泛型,以在编译时强制进行更强大的类型检查。因此,由于类型擦除,泛型类型在运行时没有任何类型信息。因此,的List<String>静态类型为,List<String>而动态类型为List

但是,数组带有组件类型的运行时类型信息。在运行时,数组使用“数组存储”检查来检查是否要插入与实际数组类型兼容的元素。因此,以下代码:

Object[] arr = new String[10];
arr[0] = new Integer(10);

会很好地编译,但由于ArrayStoreCheck而在运行时失败。对于泛型,这是不可能的,因为编译器将通过提供编译时间检查来避免运行时异常,从而避免像上面所示那样创建引用,从而避免了运行时异常。

那么,通用数组创建有什么问题?
数组,其组件类型为一个的创建类型参数,一个具体参数类型或者一个有界通配符参数化类型,是类型不安全。

考虑如下代码:

public <T> T[] getArray(int size) {
    T[] arr = new T[size];  // Suppose this was allowed for the time being.
    return arr;
}

由于T在运行时不知道的类型,因此创建的数组实际上是Object[]。因此,上述方法在运行时将如下所示:

public Object[] getArray(int size) {
    Object[] arr = new Object[size];
    return arr;
}

现在,假设你将此方法称为:

Integer[] arr = getArray(10);

这是问题所在。你刚刚将分配Object[]给的引用Integer[]。上面的代码可以正常编译,但在运行时将失败。

这就是为什么禁止通用数组创建的原因。

为什么类型转换new Object[10]到E[]作品?
现在你最后一个疑问,为什么下面的代码起作用:

E[] elements = (E[]) new Object[10];

上面的代码具有与上述相同的含义。如果你注意到了,当你将类型转换转换为未知组件类型的数组时,编译器将向你发出未经检查的转换警告。这意味着强制转换可能会在运行时失败。例如,如果你在上述方法中具有该代码:

public <T> T[] getArray(int size) {
    T[] arr = (T[])new Object[size];        
    return arr;
}

然后调用像这样调用它:

String[] arr = getArray(10);

这将在运行时失败,并带有ClassCastException。因此,没有这种方法将永远无法正常工作。

创建类型数组List []呢?
问题是一样的。由于类型擦除,a List []只是a List[]。因此,在允许创建此类数组的情况下,让我们看看会发生什么:

List<String>[] strlistarr = new List<String>[10];  // Won't compile. but just consider it
Object[] objarr = strlistarr;    // this will be fine
objarr[0] = new ArrayList<Integer>(); // This should fail but succeeds.

现在,上述情况下的ArrayStoreCheck将在运行时成功,尽管它应该引发ArrayStoreException。这是因为List []和List []都List[]在运行时编译到。

那么我们可以创建无界通配符参数化类型的数组吗?
是。原因是,a List<?>是可更改的类型。这是有道理的,因为根本没有关联的类型。因此,没有由于类型擦除而造成的损失。因此,创建此类数组是完全类型安全的。

List<?>[] listArr = new List<?>[10];
listArr[0] = new ArrayList<String>();  // Fine.
listArr[1] = new ArrayList<Integer>(); // Fine

以上两种情况都很好,因为List<?>是泛型类型的所有实例化的超级类型List 。因此,它不会在运行时发出ArrayStoreException。原始类型数组也是如此。由于原始类型也是可更改类型,因此可以创建array List[]。

因此,就像你只能创建可验证类型的数组,而不能创建不可验证类型的数组。请注意,在上述所有情况下,数组的声明都是可以的,这是使用new运算符创建数组的过程,这会产生问题。但是,没有必要声明这些引用类型的数组,因为它们只能指向null(忽略无边界类型)。

有什么解决方法E[]吗?

是的,你可以使用以下Array#newInstance()方法创建数组:

public <E> E[] getArray(Class<E> clazz, int size) {
    @SuppressWarnings("unchecked")
    E[] arr = (E[]) Array.newInstance(clazz, size);

    return arr;
}

需要类型转换,因为该方法返回一个Object。但是你可以确定这是安全的。因此,你甚至可以在该变量上使用@SuppressWarnings



 类似资料:
  • 问题内容: 由于Java泛型的实现,因此不能有以下代码: 如何在保持类型安全的同时实现此目的? 我在Java论坛上看到了这样的解决方案: 但是我真的不知道发生了什么。 问题答案: 我不得不问一个问题:您的GenSet“已选中”还是“未选中”?那是什么意思? 检查:强打字。GenSet明确地知道什么类型的包含对象(即它的构造是明确要求有Class 参数,当他们通过了类型不是参数的方法会抛出异常E。见

  • 问题内容: 该代码似乎不起作用,它将抛出异常: java.lang.ClassCastException:[Ljava.lang.Object; 无法转换为… 有人可以告诉我如何创建具有通用类型的数组吗?谢谢。 问题答案: 您不能:必须将类作为参数传递:

  • 问题内容: 我想使用simpleJdbcInsert类和executeBatch方法 http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/jdbc/core/simple/SimpleJdbcInsert.html 所以我需要传递一个as参数数组。如何创建这样的数组?我试过的是 错误:无法创建通用数组 A

  • 问题内容: 我正在尝试在java中创建一个通用数组-在其中我遇到了一些问题-我如何制作一个大小为6且里面有一个byte []和一个Integer的元组数组? 谢谢 问题答案: 好吧,您可以使用原始类型: 或者,您可以进行未经检查的转换: 或者,您也可以使用列表: 我建议改用列表。 在前两个选项之间进行选择,我建议您选择未经检查的转换,因为它将为您提供编译时检查。但是,如果将其他类型的元组放入其中,

  • 问题内容: 我正在尝试做这样的事情: myObject是一个类。我收到此错误:-通用数组创建(箭头指向new。) 问题答案: 你不能有泛型类的数组。Java根本不支持它。 你应该考虑使用集合而不是数组。例如, 另一个“解决方法”是创建这样的辅助类 然后创建一个数组MyObjectArrayList。 这是一篇很好的文章,说明了为什么在语言中不允许这样做。本文提供了以下示例,说明如果允许的话可能发生

  • 我想使用简单的Jdbc插入类和执行批处理方法 http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/jdbc/core/simple/SimpleJdbcInsert.html 所以我需要传递一个< code>Map数组 这是错误:无法创建 Map 的通用数组