Инициализация Java-массива с типом

Следующий код меня смущает:

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;

Я пытаюсь понять, почему две инициализации отличаются друг от друга. Первый - это объявление, а второе - ярлык. Оба объявлены как Object[]

Мое наивное понимание таково:

Object[] arr2 = {"a", "b", "c"}; // is a syntax sugar of
Object[] arr2 = new Object[] {"a", "b", "c"};

Таким образом, тип времени выполнения arr2 равен Object[], который не может быть преобразован в String[].

Но здесь все странно, потому что Java Array является ковариантным: String[] является подклассом Object[], а arr2 является точно a String[], отключение от Object[] до String[] на arr2 должно работать.

Любое объяснение этому высоко ценится.

Ответ 1

Поскольку 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[] на основе вашего объявления.

Ответ 2

arr2 - это точно строка []

Нет, это не так: Object[], как вы сказали, ваша строка эквивалентна:

Object[] arr2 = new Object[] {"a", "b", "c"};

Это a 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());

Ответ 3

Почти правильно. Ваша логика здесь испорчена:

arr2 is exactly a String[]

Нет, это не так. Это массив объектов. Тот факт, что вы только что добавили Strings в этот массив, не имеет смысла. Вы могли написать

arr2 = {"a", new Integer(5) };

тоже.

Вероятно, это делает его более понятным, почему вам не разрешено использовать такой массив для String [].

Ответ 4

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"};