Ошибка Eclipse? Включение нулевого значения только в случае по умолчанию

Я экспериментировал с enum, и я обнаружил, что следующие компиляции и отлично работают в Eclipse (Build id: 20090920-1017, не уверен в точной версии компилятора):

public class SwitchingOnAnull {
    enum X { ,; }
    public static void main(String[] args) {
        X x = null;
        switch(x) {
            default: System.out.println("Hello world!");
        }
    }
}

При компиляции и запуске с Eclipse, это печатает "Hello world!" и обычно выходит.

С помощью компилятора javac это выдает NullPointerException, как ожидалось.

Итак, есть ли ошибка в компиляторе Java Eclipse?

Ответ 1

Это ошибка. Здесь указано поведение оператора switch в соответствии с Спецификацией языка Java, 3-е издание:

JLS 14.11 Заявление switch

SwitchStatement:
    switch ( Expression ) SwitchBlock

Когда выполняется оператор switch, сначала оценивается Expression. Если Expression оценивается как null, a NullPointerException выбрасывается, и по этой причине завершается весь оператор switch.

По-видимому, ошибка в Eclipse не имеет никакого отношения к случаю default или enum вообще.

public class SwitchingOnAnull {
    public static void main(String[] args) {        
        java.math.RoundingMode x = null;
        switch(x) {};

        switch((Integer) null) {};

        switch((Character) null) {
            default: System.out.println("I've got sunshine!");
        }       
    }
}

Вышеприведенный код компилируется и запускается "отлично" (по крайней мере, в некоторой версии) Eclipse. Каждый switch выделяет NullPointerException при компиляции с javac, который точно соответствует мандату спецификации.


Причина

Здесь javap -c SwitchingOnAnull при компиляции в Eclipse:

Compiled from "SwitchingOnAnull.java"
public class SwitchingOnAnull extends java.lang.Object{
public SwitchingOnAnull();
Code:
 0: aload_0
 1: invokespecial  #8; //Method java/lang/Object."<init>":()V
 4: return

public static void main(java.lang.String[]);
Code:
 0: aconst_null
 1: astore_1
 2: getstatic     #16; //Field java/lang/System.out:Ljava/io/PrintStream;
 5: ldc           #22; //String I've got sunshine!
 7: invokevirtual #24; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
10: return

}

Кажется, что компилятор Eclipse полностью избавляется от целых конструкций switch. К сожалению, эта оптимизация нарушает спецификацию языка.


Официальные слова

Ошибка исправлена ​​и назначена для исправления.

Оливье Томман 2010-05-28 08:37:21 EDT

Мы слишком агрессивны в оптимизации.

Для:

  switch((Integer) null) {};

мы оптимизируем весь оператор switch, когда мы должны хотя бы оценить выражение.

Я посмотрю.

Кандидат на 3.6.1.

См. также

Ответ 2

Определенно. Если мы рассмотрим главу 14.11 спецификации java языка, она четко заявляет (в разделе "обсуждение" ):

Запрет на использование null как метка коммутатора предотвращает писать код, который никогда не может быть казнены. Если выражение переключателя ссылочного типа, например, в коробке примитивный тип или перечисление, время выполнения ошибка возникает, если выражение оценивает значение null во время выполнения.

Ответ 3

Угу. Согласно JLS это ошибка:

Если выражение switch имеет ссылочный тип, такой как примитивный тип или перечисление в штучной упаковке, будет выполняться ошибка времени выполнения, если выражение будет равно нулю во время выполнения.