10

当我执行以下操作时,

  • arrayList1- 包含一个元素,它是一个int[].
  • arrayList2- 未编译(错误:构造函数ArrayList<Integer>(List<int[]>)未定义)
  • arrayList3- 包含 7 个元素,它们是Integer对象

这是代码:

int[] intArray = new int[]{2,3,4,5,6,7,8};
ArrayList arrayList1 = new ArrayList(Arrays.asList(intArray));
ArrayList<Integer> arrayList2 = new ArrayList<Integer>(Arrays.asList(intArray));

Integer[] integerArray = new Integer[]{2,3,4,5,6,7,8};
ArrayList<Integer> arrayList3 = new ArrayList<Integer>(Arrays.asList(integerArray));

问题: 为什么编译器不自动装箱int[]to中的元素Integer并创建一个ArrayList<Integer>?这背后的原因是什么?是我的愚蠢还是其他原因?

4

5 回答 5

14

不同之处int[]在于它本身是一个Object,而它是一个对对象Integer[]的引用数组。Integer

Arrays.asList(T...)方法接受某种类型的变量参数,T没有上限。该方法的擦除是Arrays.asList(Object...)。这意味着它将采用从Object.

由于intis 不是 an Object,而是原始类型,因此它不能作为 的单个元素传递T[],而int[]is anObject本身,它将作为T[]数组的第一个元素(T...内部T[]仅是 a )。但是,Integer[]将作为 传递T[],每个引用Integer[]作为不同的参数传递给T[]

即使您认为编译器应该完成从int[]数组的每个元素到的转换Integer,这对编译器来说也太过分了。首先,它需要获取每个数组元素,并将其装箱到Integer,然后它需要Integer[]从这些元素内部创建一个。这实在是太多了。它已经具有从int[]to的直接转换Object,它遵循。虽然我一直希望 Java 允许从int[]to进行隐式转换Integer[],这样在使用泛型时会变得更简单,但同样,这就是语言的设计方式。

举个简单的例子:

Object[] array = new Integer[10];  // this is valid conversion
Object[] array2 = new int[10];     // this is not
Object obj = new int[10];          // this is again a valid conversion

因此,在您的代码中Arrays.asList(intArray)返回 aArrayList<int[]>而不是ArrayList<Integer>。您不能将其传递给ArrayList<Integer>()构造函数。


有关的:

于 2013-10-22T04:16:39.367 回答
3

一个int[]等于一个Integer[]

数组具有关联的 Class 对象。 原始整数数组的类对象是[I. 数组的类对象Integer[Ljava/lang/Integer

数组本身就是一个对象,因此在两个相同类型的对象之间进行转换是一种身份转换。正如上面的字节码所证明的那样,在两个不同类型的对象之间进行转换不是——而且int[]肯定是不同的。Integer[]

最后,请记住,自动装箱只有在有关联的装箱转换时才会真正适用。

于 2013-10-22T04:29:28.483 回答
3

从技术上讲,当然可以做到。然而,原始类型数组到包装类型数组的自动装箱/拆箱超出了您的预期。

首先看一下 Java 的自动装箱/拆箱:它所做的只是一个语法糖,让您无需输入原始包装器代码。例如

Integer i = 10;

编译器知道它期待一个Integer, 而是int存在。因此,编译器所做的是将您的代码转换为:

Integer i = Integer.valueOf(10);

它对拆箱做了类似的事情:当它期望intInteger存在的情况下,编译器将其替换为varName.intValue()

回到数组。我们可以预见两个问题:

第一个问题是,没有直接的方法可以从 int 数组转换为 Integer 数组。您可能会争辩说编译器可以转换

int[] intArray = ....;
Integer[] wrapperArray = intArray ;

Integer[] wrapperArray = new Integer[intArray.size()];
for (int i = 0; i < intArray.size(); i++) {
   wrapperArray[i] = Integer.valueOf(intArray[i]);
}

但这对于语法糖来说似乎太多了。

第二个大问题是,当您将它作为参数传递给方法时,如果对数组进行自动装箱/拆箱,而不是传递原始数组的引用,您现在传递的是原始数组副本的引用。如果您在方法中更改数组的内容,则原始数组不会受到影响。这可以给你带来很多惊喜。

例如

void foo(Integer[] arr) {
    arr[0] = 0;
}

// invoking foo in some code:
int[] intArr = new int[]{9,8,7,6};
foo(intArr);
// intArr[0] will still be 9, instead of 0
于 2013-10-22T04:32:30.927 回答
1

因为int[]Integer[]都是对象。First 将保存原始int值,它们不是 type Object,而 second 将存储Integer对象的引用,它们是 type Object

于 2013-10-22T04:18:44.553 回答
1

arrayList1真的是一个大小的列表。

http://ideone.com/w0b1vY

arrayList1.size() = 1
arrayList3.size() = 7

int[] 被强制转换为单个对象。该对象不能强制转换为整数。

于 2013-10-22T04:47:10.010 回答