以下代码让我感到困惑:
Object[] arr1 = new String[]{"a", "b", "c"};
Object[] arr2 = {"a", "b", "c"};
String[] a = (String[]) arr1; // ok
String[] b = (String[]) arr2; // ClassCastException
System.out.println(arr1.getClass().getName()); // [Ljava.lang.String;
System.out.println(arr2.getClass().getName()); // [Ljava.lang.Object;
我试图理解为什么这两个初始化彼此不同。第一个是post声明,而第二个是快捷方式。两者均声明为Object[]
我天真的理解是:
Object[] arr2 = {"a", "b", "c"}; // is a syntax sugar of
Object[] arr2 = new Object[] {"a", "b", "c"};
因此,arr2
的运行时类型正是Object[]
,不能转换为String[]
。
但这里的情况变得很奇怪,因为Java数组是协变的:String[]
是Object[]
的一个子类,而arr2
正是String[]
,在arr2
上从Object[]
向后转换到String[]
应该可以。
对此的任何解释都将不胜感激。
因为arr2
是Object[]
,所以没有什么可以阻止您编写
arr2[0] = new Object();
就在你的演员阵容之前,在这种情况下,演员阵容无论如何都不再有意义。
由于初始化器语法的工作方式,还需要注意以下内容:
Object x = {"a", "b"}; // error: illegal initializer for Object
Object[] a = {"a", "b"}; //a has class [Ljava.lang.Object;
String[] b = {"a", "b"}; //b has class [Ljava.lang.String;
编译器根据您的声明来确定您希望数组是Object[]
还是String[]
。
arr2正是String[]
不,它不是——正如你所说,它是Object[]
——你的线路相当于:
Object[] arr2 = new Object[] {"a", "b", "c"};
这是一个Object[]
,恰好有元素,这些元素目前都是字符串引用。。。但你也可以写:
arr2[0] = new Object(); // Fine, because arr2 is an Object[]
如果你对arr1
做了同样的事情,你会得到一个例外:
arr1[0] = new Object(); // Fine at compile time, will throw an exception
您可以使用getClass
检查对象的实际执行时间类型:
System.out.println(arr2.getClass());
几乎正确。你的逻辑有缺陷:
arr2 is exactly a String[]
不,不是。它是一个对象数组。您刚刚将Strings添加到此数组中这一事实毫无意义。你本可以写
arr2 = {"a", new Integer(5) };
也是。
这可能会更清楚地说明为什么不允许将这样的数组强制转换为String[]。
Object[] arr2 = {"a", "b", "c"};
在这种情况下,您声明的数组等于
Object[] arr2 = new Object[] {"a", "b", "c"};
因此,arr2中的元素可以是任何类型的Object
。在线上
String[] b = (String[]) arr2; // ClassCastException
因为您正在尝试将整个Object[]
强制转换为String[]
。
和第一个一样,你明确地告诉所有对象都是字符串
Object[] arr1 = new String[]{"a", "b", "c"};