对象数组和字符串数组之间有什么区别

时间:2011-12-06 12:59:16

标签: java arrays covariance

这有效:

Object[] array = new Object[3];  
array[0] = "ddd";  
array[1] = new Integer(12);  

这不会:(在新整数时崩溃)

Object[] array2 = new String[3];  
array2[0] = "ddd";  
array2[1] = new Integer(12);  

我已经阅读了covariance,但仍然无法理解第二个代码示例被禁止的基本技术原因,或者为什么抛出ArrayStoreException。 一个Object引用数组和一个String引用数组之间的区别是什么?

我理解在第二个例子中,数组被实例化以便向其添加字符串,但是,某些东西不会逻辑地点击。有人能用简单的方法解释一下吗?

4 个答案:

答案 0 :(得分:3)

Object引用可以包含任何类型的Object,而String引用只能包含String - 并且JVM将在运行时检查它。实际上没有什么比这更重要了。

你的第二个例子实际上指出了Java语言设计的一个缺陷。这个异常在运行时可能会发生,这显然很糟糕!泛型功能的设计略有不同,因此您不能对通用集合产生同样的问题。令人遗憾的是,String[]是一个Object[];但List<String>不是List<Object>

答案 1 :(得分:1)

在:

Object[] array2 = new String[3];  

引用的类型为Object[],但引用引用的实际对象的类型为String[]

当您尝试将(在您的情况下)Integer类型对象分配给数组时,将取消引用该引用并检查实际对象。在第二种情况下,数组对象的类型与要插入的对象的类型不匹配。

当然,这只能在运行时被发现,因为一旦程序执行,编译器就不能确定对象array2将指向哪个(技术上它可以在这个简单的程序中,但不是在一般情况下)。

答案 2 :(得分:1)

协方差的简单定义:如果存在具有类型参数Container&lt; T&gt;的通用容器类,并且X是T的子类,则Container&lt; X&gt;是Container&lt; T&gt;的子类。

经典的例子是List。如果列表在Java中是可变的,则List&lt; String&gt;将是List&lt; Object&gt;的一个sublcass,因为String是Object的子类。但是列表在Java中不是协变。数组协变。

协方差是一个有用的想法,但在某些情况下会导致问题。

String[] stringArray = new String[1];
Object[] objectArray = stringArray;
objectArray[0] = new Integer(4);
System.out.println("value=" + stringArray[0]); // what should this do?

因为我们可以分配objectArray = stringArray(因为它是协变的),所以我们可以将任何类型的对象分配给objectArray [0],这违反了你在声明String []时所拥有的'契约'。这不能被编译器捕获。 Java通过以下方式避免了这种情况:

  1. 当您尝试分配与定义的数组类型不匹配的值时抛出ArrayStoreException(上面的第3行)
  2. 让其他课程保持不变。
  3. 数组是协变的,以便通用允许编写排序和搜索。请参阅Arrays#sort()

    Scala等语言允许程序员选择容器类是协变的,逆变的还是简单的不变量。

答案 3 :(得分:1)

让我对你的第二个例子做一个小改动来说明这是真正的:

String[] array1 = new String[3];
Object[] array2 = array1;
array2[0] = "ddd";  
array2[1] = new Integer(12);  // this one

让我们假设已标记的语句有效。 (它不......但我们假设......)

如果我这样做会怎么样?

String wot = array1[1];  

该赋值语句应该是静态类型安全的,因为array1被声明为String数组。但它肯定不是动态类型安全的!简而言之,我们刚刚以一种非常基本的方式打破了Java静态类型。

Java避免这种破坏是通过将运行时类型检查作为标记语句的一部分进行,并且不允许将非String分配到String[]对象的元素中。