Java generics SuppressWarnings ( "unchecked" ) тайна
Почему код альтернативный (1) компилируется без предупреждений, а альтернативный код (2) выдает предупреждение "непроверенного броска"?
Общий для обоих:
class Foo<T> {
Foo( T [] arg ) {
}
}
Альтернатива (1):
class Bar<T> extends Foo<T> {
protected static final Object [] EMPTY_ARRAY = {};
@SuppressWarnings("unchecked")
Bar() {
super( (T []) EMPTY_ARRAY );
}
}
Альтернатива (2):
class Bar<T> extends Foo<T> {
@SuppressWarnings("unchecked")
Bar() {
super( (T []) EMPTY_ARRAY );
}
protected static final Object [] EMPTY_ARRAY = {};
}
Альтернатива (2) производит:
javac -Xlint:unchecked Foo.java Bar.java
Bar.java:4: warning: [unchecked] unchecked cast
super( (T []) EMPTY_ARRAY );
^
required: T[]
found: Object[]
where T is a type-variable:
T extends Object declared in class Bar
1 warning
Это:
java version "1.7.0_07"
Java(TM) SE Runtime Environment (build 1.7.0_07-b10)
Java HotSpot(TM) 64-Bit Server VM (build 23.3-b01, mixed mode)
Ответы
Ответ 1
Я не могу ничего найти в JLS, @SuppressWarnings
(JLS 9.6.3.5) и непроверенных предупреждений (JLS 5.1.9), разделы, похоже, не имеют проблем, которые могут привести к этой проблеме. Моя догадка (без тестирования вашего SSCE сама) заключается в том, что вы нашли ошибку в компиляторе. Я бы рекомендовал отправить отчет об ошибке в Oracle и добавить ссылку на ваш вопрос.
Короче говоря, порядок членов в классе должен быть полностью независимым в отношении того, как обрабатываются предупреждения. Это может быть крайний случай только для неконтролируемого кода предупреждения, или это может быть большая проблема.
В то же время вы можете устранить все свои проблемы, выполнив то, что вы должны были сделать в первую очередь, и динамически генерировать пустой массив вместо того, чтобы использовать существующий, как описано в этот вопрос.
Edit
Я не вижу, как связанное предложение будет работать в случае моего EMPTY_ARRAY
, который является static final
.
Не делайте это static final
больше и предоставляйте Class<T>
в своем конструкторе:
@SuppressWarnings("unchecked") // Still need this
public Bar(Class<T> clazz) {
super((T[]) Array.newInstance(clazz, 0));
}
Java почти никогда не использует значение переменной final
для предупреждений, за исключением случаев с мертвым кодом. В противном случае вы получите такие крайние случаи:
class Bar<T> extends Foo<T> {
// Is it really empty?
protected static final Object [] EMPTY_ARRAY = SomeOtherClass.getEmptyArray();
@SuppressWarnings("unchecked")
Bar() {
super( (T []) EMPTY_ARRAY );
}
}
Им придется написать эту логику в компилятор. Это ненужное осложнение для краевых случаев, таких как "пустые массивы", и, кроме того, это кастинг, подобный этому, в конечном итоге вызывает запах кода.
Другой вариант, который может возникнуть помимо этого ответа, - использовать var args. Foo
:
class Foo<T> {
Foo( T ... arg ) {
}
}
И Bar
:
class Bar<T> extends Foo<T> {
Bar() {
super();
}
}
Это должно работать, и оно устраняет все кастинга, пустые массивы, предупреждения и т.д. Подробнее о var args и их возможных вызовах здесь.
Ответ 2
Я могу подражать этому странному поведению на моем компьютере с Windows 7 64b с помощью:
-
Java(TM) SE Runtime Environment (build 1.7.0_02-b13)
-
OpenJDK Runtime Environment (build 1.8.0-ea-lambda-nightly-h1669-20121030-b63-b00)
Это означает, что затронуты как OpenJDK, так и Oracle JDK, как JDK7, так и JDK8 (да, вы уже можете скачать его).
Eclipse, поскольку он использует собственный JDT-компилятор, не имеет этой проблемы.
Так кажется, что это действительно ошибка javac
. Если вы сообщите об этом, пожалуйста, держите меня в курсе.
EDIT:
Я также установил установку JDK6 на моем компьютере, поэтому я попробовал это и фактически, он работает без предупреждения в обоих случаях, что является правильным поведением:
-
Java(TM) SE Runtime Environment (build 1.6.0_23-b05)
Хотя мои Windows - 64b, все упомянутые JDK - всего 32b.
Ответ 3
Я смог воспроизвести поведение с этой упрощенной настройкой:
class Bar<T> {
@SuppressWarnings("unchecked")
Bar() {
T[]dummy = (T[]) EMPTY_ARRAY;
}
private static final Object [] EMPTY_ARRAY = {};
}
Как предположил Брайан, это похоже на ошибку в компиляторе. Кроме того, это поведение ограничено массивами - замена EMPTY_ARRAY на Object
и отбрасывание его на T
не выдает предупреждения, как ожидалось.
java version "1.7.0_09"
Java(TM) SE Runtime Environment (build 1.7.0_09-b05)
Java HotSpot(TM) 64-Bit Server VM (build 23.5-b02, mixed mode)
Ответ 4
Это ошибка как в Oracle, так и в OpenJDK 7 и 8.
Вы не можете (не должны) компилировать предупреждения/ошибки из переопределения объявлений в классе.
Ошибки времени выполнения, да; ошибки компилятора, нет.
Это Ошибка 8016636 (спасибо за его регистрацию), но за год не было никакой активности.
К сожалению, это не редкость в Oracle bug tracker.
Дополнительная странность:
-
Предупреждение подавляется, если EMPTY_ARRAY
не final
.
-
По иронии судьбы, предупреждение подавляется, если вы инициализируете его не-массивом, например. Object EMPTY_ARRAY = new Object()
. (Но не делайте этого...)