40

假设我必须创建一个数组来存储 ArrayList 的整数,并且数组大小为 10。

下面的代码将做到这一点:

ArrayList<Integer>[] pl2 = new ArrayList[10]; 

问题一:

在我看来,更合适的代码是

ArrayList<Integer>[] pl2 = new ArrayList<Integer>[10];    

为什么这不起作用?

问题2:

以下都编译

  1. ArrayList<Integer>[] pl2 = new ArrayList[10];
  2. ArrayList[] pl3 = new ArrayList[10];

pl2就and的引用声明而言,有什么区别pl3

4

11 回答 11

16

泛型信息仅在编译时很重要,它告诉编译器可以将哪种类型放入数组中,在运行时,所有泛型信息都将被删除,所以重要的是如何声明泛型类型。

引自 Think in Java:

说您不能创建泛型类型的数组并不完全正确。没错,编译器不会让您实例化泛型类型的数组。但是,它将允许您创建对此类数组的引用。例如:

List<String>[] ls; 

这毫无怨言地通过了编译器。虽然你不能创建一个包含泛型的实际数组对象,但你可以创建一个非泛型类型的数组并转换它:

//: arrays/ArrayOfGenerics.java 
// It is possible to create arrays of generics. 
import java.util.*; 

public class ArrayOfGenerics { 
    @SuppressWarnings("unchecked") 
    public static void main(String[] args) { 
        List<String>[] ls; 
        List[] la = new List[10]; 
        ls = (List<String>[])la; // "Unchecked" warning 
        ls[0] = new ArrayList<String>(); 
        // Compile-time checking produces an error: 
        //! ls[1] = new ArrayList<Integer>(); 

        // The problem: List<String> is a subtype of Object 
        Object[] objects = ls; // So assignment is OK 
        // Compiles and runs without complaint: 
        objects[1] = new ArrayList<Integer>(); 

        // However, if your needs are straightforward it is 
        // possible to create an array of generics, albeit 
        // with an "unchecked" warning: 
        List<BerylliumSphere>[] spheres = 
           (List<BerylliumSphere>[])new List[10]; 
        for(int i = 0; i < spheres.length; i++) 
           spheres[i] = new ArrayList<BerylliumSphere>(); 
    } 
}

一旦你引用了一个 List[],你可以看到你得到了一些编译时检查。问题是数组是协变的,因此 List[] 也是 Object[],您可以使用它来将 ArrayList 分配给您的数组,在编译时或运行时都不会出错。

但是,如果您知道自己不会向上转型并且您的需求相对简单,那么可以创建一个泛型数组,这将提供基本的编译时类型检查。但是,泛型容器实际上总是比泛型数组更好的选择。

于 2013-05-07T09:15:40.683 回答
13

问题一:

基本上,这是 Java 语言所禁止的。这在Java Language Specification for generics中有介绍。

当你使用

ArrayList<Integer>[] pl2 = new ArrayList[10];    // warning

您会收到编译器警告,因为以下示例将编译(为每一行代码生成警告):

ArrayList wrongRawArrayList = new ArrayList();      // warning
wrongRawArrayList.add("string1");                   // warning 
wrongRawArrayList.add("string2");                   // warning  

pl2[0] = wrongRawArrayList;                         // warning 

但是现在你应该包含ArrayListof 的数组Integer包含完全错误ArrayListString对象。

问题2:

正如已经回答的那样,声明p12为您提供编译时检查,并使您在从ArrayList.

稍微修改了前面的例子:

ArrayList<Integer>[] pl2 = new ArrayList[10];                // warning 

ArrayList<String> wrongArrayList = new ArrayList<String>();  // OK!
wrongArrayList.add("string1");                               // OK! 
wrongArrayList.add("string2");                               // OK!

pl2[0] = wrongArrayList;                                     // ERROR

现在,由于您使用的是泛型,因此无法编译。但是如果你使用

ArrayList[] pl2 = new ArrayList[10]; 

您将得到与第一个示例相同的结果。

于 2013-05-07T09:27:08.770 回答
4

数组是协变的。这意味着它们在运行时保留其元素的类型。Java 的泛型不是。他们使用类型擦除基本上掩盖了正在进行的隐式转换。理解这一点很重要。

你需要使用Array.newInstance()

此外,数组携带有关其组件类型的运行时类型信息,即有关所包含元素的类型的信息。当元素存储在数组中时,使用有关组件类型的运行时类型信息,以确保不会插入“外来”元素。

更多详情请看这里

于 2013-05-07T09:24:21.590 回答
2

让我们先从问题 2 开始,然后再回到问题 1:

问题2:

> ArrayList[] pl2 = 新的 ArrayList[10]; ArrayList[] pl3 = new ArrayList[10];

就 p12 和 p13 的引用声明而言,有什么区别?

在 pl2 中确保比 p13 更好的类型安全性。

如果我为 pl2 写作:

pl2[0]=new ArrayList<String>();

它会给我一个编译器错误,说明“无法转换ArrayList<String>ArrayList<Integer>

因此,它确保了编译时的安全性。

但是,如果我为 p13 写作

pl3[0]=new ArrayList<String>();
pl3[1]=new ArrayList<Integer>();

它不会抛出任何错误,开发人员有责任在从 p13 提取数据时正确编码和检查,以避免在运行时发生任何不安全的类型转换。

问题一:

这可能就是泛型的工作方式。在主数组初始化期间ArrayList<Integer>[] pl2 = new ArrayList[10],左侧的 ,ArrayList<Integer>[] pl2仅当您在索引位置初始化 ArrayList 对象时,才会确保类型安全:

pl2[0]=new ArrayList<Integer>();

右侧的主数组声明= new ArrayList[10]只是确保索引位置将包含 ArrayList 类型的项目。另请查看Type Erasure中的类型擦除概念以获取更多信息。

于 2013-05-07T09:37:53.283 回答
2

这不起作用,因为泛型类不属于Reifiable Types

关于数组创建表达式的JLS指出:

如果[class type] 不表示可具体化的类型(第 4.7 节),则会出现编译时错误。否则,[类类型] 可以命名任何命名的引用类型,甚至是抽象类类型(第 8.1.1.1 节)或接口类型(第 9 节)。

上面的规则意味着数组创建表达式中的元素类型不能是参数化类型,除了无界通配符

Reifiable Types的定义是:

由于某些类型信息在编译过程中会被删除,因此并非所有类型在运行时都可用。在运行时完全可用的类型称为可具体化类型。

当且仅当满足以下条件之一时,类型才是可具体化的:

It refers to a non-generic class or interface type declaration.

It is a parameterized type in which all type arguments are unbounded wildcards (§4.5.1).

It is a raw type (§4.8).

It is a primitive type (§4.2).

It is an array type (§10.1) whose element type is reifiable.

It is a nested type where, for each type T separated by a ".", T itself is reifiable.

For example, if a generic class X<T> has a generic member class Y<U>, then the type X<?>.Y<?> is reifiable because X<?> is reifiable and Y<?> is reifiable. The type X<?>.Y<Object> is not reifiable because Y<Object> is not reifiable.
于 2013-05-07T10:23:02.747 回答
1

问题 1。

好吧,这不是正确的语法。因此,这是行不通的。

问题2。

ArrayList<Integer>[] pl2 = new ArrayList[10];
ArrayList[] pl3 = new ArrayList[10];

由于 pl2 在编译时使用泛型类型 <Integer> 定义,编译器将知道 pl2 只允许具有 Integers,如果您尝试分配 Integers 以外的其他内容,您将收到警告并且编译将失败。

在 pl3 中,由于没有泛型类型,您可以将任何类型的对象分配给列表。

于 2013-05-07T09:39:28.453 回答
0

问题1

您不能创建参数化类型的数组

问题2

 ArrayList<Integer>[] pl2 = new ArrayList[10];

这意味着你告诉编译器你将创建一个数组来存储整数的arraylist。您的 arraylist 将仅包含 Integer 对象。这就是泛型的用武之地。泛型使您的代码更加安全可靠。如果你确定你的列表应该只包含整数对象,你应该一直这样做。

但是当你说

 ArrayList[] pl3 = new ArrayList[10];

这意味着 arraylist 可以存储任何对象类型,如字符串、整数、自定义对象等。

于 2013-05-07T09:31:00.507 回答
0

据我所知,在 Java 中没有泛型之类的东西。就种类而言,ArrayList<Integer> and ArrayList都是一样的东西。

Java 对泛型使用类型擦除。这意味着有关泛型的所有类型信息都在编译时被删除。于是ArrayList<Integer>变成了ArrayList。

所以这只是一个编译时技巧。我猜,为了避免程序员可能做的任何混淆或错误,他们允许ArrayList<Integer>[]像这样实例化:new ArrayList[10].

所以 anArrayList<Integer>[]和 aArrayList[]是一回事,因为括号中的信息在编译时被删除了。

于 2013-05-07T12:22:56.020 回答
0

默认情况下,泛型问题由编译器作为警告发出。

编译后,由于类型擦除,它们都变成ArrayList[] pl2 = new ArrayList[10]了 ,但是编译器警告你这不好。

泛型已添加到 Java 中,为了向后兼容,您可以互换使用泛型和非泛型。

于 2013-05-07T09:11:07.487 回答
0

根据对 Stack Overflow 问题Create an array of ArrayList elements的回答,您似乎无法创建具有泛型类型的数组列表。

于 2013-05-07T10:41:05.883 回答
0
 ArrayList<Integer>[] pl2 = new ArrayList<Integer>[10];

意味着在正常情况下从 ArrayList 示例中检索数据时不需要进行强制转换

 ArrayList[] pl2 = new ArrayList[10];
 pl2.put(new Integer(10));
 Integer i = p12.get(0);    // this is wrong
 Integer i = (Integer)p12.get(0);    // this is true with casting

 ArrayList<Integer>[] pl2 = new ArrayList<Integer>[10];   
 pl2.put(new Integer(10));
 Integer i = p12.get(0);    // this is true no need for casting
于 2013-05-07T09:22:05.823 回答