Почему Java разрешает присвоение типа небезопасным Array? - программирование
Подтвердить что ты не робот

Почему Java разрешает присвоение типа небезопасным Array?

Как правило, Java можно рассматривать как язык, безопасный для типов. Я знаю, что есть некоторые недостатки с дженериками, но я недавно столкнулся с проблемой, которой я никогда не сталкивался. Чтобы сломать это:

Object[] objects = new Integer[10];
objects[0] = "Hello World";

НЕ приведет к ошибке времени компиляции, как ожидалось. Я бы предположил, что объявление массива Object запретит указывать на массив чего-то другого. В Generics мне не разрешают делать такие странные вещи, как:

ArrayList<Object> objs = new ArrayList<Integer>

и если я попытаюсь сделать трюк Java, чтобы что-то сделать с

ArrayList<? extends Object> objects = new ArrayList<Integer>

Мне разрешено объявлять его, но я могу добавить только объекты типа null.

Почему Java не препятствует объявлению таких массивных массивов?

4b9b3361

Ответ 1

Я не думаю, что есть ответ на этот вопрос, помимо "устаревшего дизайна". (Которое я признаю, это причудливый способ сказать "потому что".) Вы в значительной степени должны иметь возможность сделать эквивалент последнего задания, которое вы как-то показываете. (В противном случае вы застряли в том, чтобы делать много и много копий с ручным переходом вверх/вниз, предполагая языковые особенности Java до 1.4)

В Java 1, когда семантика семантики для массивов была в основном задана в камне, дженерики не были доступны или еще долго для рассмотрения. Таким образом, не было механизма, позволяющего выразить ограничения типа высшего порядка, необходимые для того, чтобы сделать эту конструкцию безопасным по типу - и Гослинг (IIRC - фанат простоты) почувствовал, что решить этот краевой случай безопасности типа компиляции не стоит сложным языком с любыми решениями. Или не беспокоился, делая проверку во время выполнения достаточно, чтобы даже искать решение. (В конце концов решения по языковому дизайну произвольны, по крайней мере, в некоторой степени, и есть только один человек, который мог бы ответить на это с какой-либо определенностью.)

Ответ 2

Во-первых, я должен указать, что это безопасно.

Object[] objects = new Integer[10];
objects[0] = "Hello World";

потому что будет выбрано исключение. (Он не является статически безопасным по типу... но это совсем другое выражение.)

Причина, по которой Java допускает это, является исторической. До Java 5 Java не поддерживал какой-либо формы дженериков. Гослинг сказал, что если бы у них было время выяснить и включить дженерики в Java 1.0, они бы это сделали.

К сожалению, они этого не сделали. Но они все еще хотели писать такие вещи, как метод сортировки общего назначения со следующей подписью:

    void sort(Object[] array, Comparator comp) ...

Чтобы этот метод работал для любого типа массива объектов (без дженериков), необходимо было сделать массивы ковариантными; т.е. сделать его законным для передачи String[] или Integer[] в качестве аргумента, где формальный тип Object[]. Если бы они не сделали этого, вам пришлось бы скопировать String[] в Object[], отсортировать его, а затем скопировать обратно.

Ответ 3

"Потому что это должно быть".

Чтобы разработать немного, рассмотрим следующий пример:

Object[] objects = null;
if (something) {
    objects = new Integer[10];
} else {
    objects = new String[10];
}

Теперь, как компилятор Java знает, какие назначения разрешить и от чего отказаться? Это невозможно. Тип времени компиляции - это Object, поэтому компилятор позволит вам помещать любой объект в ваш массив, просто потому, что он не знает о типе времени выполнения вашего массива.

Ответ 4

на самом деле в случае массивов вы получаете исключение во время выполнения, называемое ArrayStoreException, когда вы добавляете неправильный тип элемента. В этом случае Строка. В случае дженериков нет такого исключения. его для very same reason вам не разрешено добавлять ничего, кроме объекта, поскольку вы можете просто добавить неправильный тип в список.

Ответ 5

Там Обсуждение, которое я нашел, когда я его google

Найдено:

Во-первых, массивы не нарушают безопасность типа. Если бы они это сделали, то повышение массив не будет работать во время выполнения. Они делают невозможным компилятор, чтобы доказать безопасность типа программы, поэтому проверка отложена до времени выполнения.

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

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

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

Метод "подстановочных знаков", описанный в приведенной выше ссылке, является именно тем, что CommonLisp работает уже много лет.

(deftype StringArray? () (array String)) ; This is the type of arrays of String 
(deftype ObjectArray? () (array Object)) ; This is the type of arrays of Object 
(subtypep StringArray? ObjectArray?)      ; Is StringArray? a subtype of ObjectArray?? false, true                               ; No, it isn't. (false: it isn't, true: I'm ure) 
(deftype AnyArray? () (array *))         ; This is the type of arrays of anything (subtypep StringArray? AnyArray?)         ; Is StringArray? a subtype of AnyArray??   true, true                                ; Yes, it is. (true: it is, true: I'm sure)