Обновление: этот ответ получил больше внимания и внимания, чем я думаю, что он заслуживает в основном копирования исходного кода JDK, поэтому я попытаюсь превратить его в нечто достойное.
Java дженериков разработаны, чтобы выглядеть и чувствовать себя как настоящие, овеществленная, мульти-экземпляр, C++ - или C# -стиль дженериков. Это означает, что для такого типа, как ArrayList<E>
, мы ожидаем, что ArrayList<String>
будет вести себя так, как будто в каждом случае E
был заменен на String
. Другими словами, это:
private Object[] elementData = new Object[size];
public E get(int i) {
return (E) elementData[i];
}
String str = list.get(0);
должно стать следующее:
private Object[] elementData = new Object[size];
public String get(int i) {
return (String) elementData[i];
}
String str = list.get(0);
Теперь, как вы, наверное, знаете, что это на самом деле не как они работают. По соображениям обратной совместимости, которые сейчас (в основном) давно позади нас, Java-дженерики реализуются с помощью стирания типа, где E
фактически заменяется на Object
всюду, а листы до String
вставляются в , вызывая код, где это необходимо. Это означает, что код на самом деле становится чем-то вроде этого:
private Object[] elementData = new Object[size];
public Object get(int i) {
return elementData[i];
}
String str = (String) list.get(0);
Актерский к (E)
исчез, и снова появился на месте вызова. Если сайт вызова проигнорировал результат, бросок исчез бы полностью! Вот почему он дал «непроверенное» предупреждение.
Теперь представьте себе, если elementData
имел тип E[]
вместо этого, как вы предлагаете. То есть, код выглядит следующим образом:
private E[] elementData = (E[]) new Object[size];
public E get(int i) {
return elementData[i];
}
String str = list.get(0);
Мы знаем, что трансформируется в одно и то же, как и выше, из-за стирания. Но если бы мы овеществлённая дженерики, как мы хотим, мы сделали, это будет выглядеть следующим образом:
private String[] elementData = (String[]) new Object[size];
// ClassCastException: Object[] is not a String[]
По существу мы написали код, который должен врезаться во времени выполнения, и единственная причина, она работает на все, что дженерики в Java реализация делает вид, что она лучше, чем есть. Мы солгали компилятору, чтобы убедить его принять хрупкий код.
И это хрупкое! Мы избегаем сбоев во время выполнения, потому что массив никогда не ускользает от класса. Но если бы это было так, это вызвало бы ClassCastException
s в труднодоступных местах. Что делать, если Java 9 представила обновленные дженерики? Первая реализация продолжила бы работать, но это сломалось бы.
Вот почему наиболее разумные соглашения о кодировании Java требуют, чтобы неконтролируемые отбрасывания были правильными по типу. (E) elementData[i]
является правильным, потому что ArrayList
гарантирует, что только E
s может быть сохранен в elementData
. (E[]) new Object[size]
никогда не соответствует правилу, если E
не является Object
.
Есть и другие преимущества. В Java 8, elementData
поле может принимать специальные значения дозорных:
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
Вы, кажется, думают, что «E» является реальным. Это Финг Ньютон вашего воображения. Класс массива фиксирован в то время, когда класс ArrayList компилируется, обратно в литейный цех Java. –
@HotLicks «Я понимаю, что после запуска компилятора тип-стирание преобразует E [] в Object []« .. »Если использовать E [] этот код ниже [это не обязательно] [использовать приведения] – user2864740
Возможно, исходный код был улучшен, объявив E [], но окончательный результат компиляции был бы таким же. –