Switch文の条件式にenumじゃなくてnullを入れてみた

昨日、ふと思ったんですが・・・。


JavaのSwitch文の条件式には数値型だけでなく、列挙型(enum)も使うことができます。
でも、enumはクラスの一種なので列挙型の変数にはnullも入れられます。
そんなnullが入っている列挙型変数を、Switchの条件式に入れたらどうなるのでしょうか。


・・・予想がつかないので、やってみました。

public class SwitchTest {
	private enum Signal {GREEN, YELLOW, RED};
	
	public static void main(String args[]){
		CallSignal(null);
	}
	
	private static void CallSignal(Signal signal){
		switch(signal){
		case GREEN:
			System.out.println("青信号");
			break;
		case YELLOW:
			System.out.println("黄信号");
			break;
		case RED:
			System.out.println("赤信号");
			break;
		default:
			System.out.println("無灯火・・・?");
			break;
		}
	}
}

実行結果はswitch(signal)のところ(9行目)でNullPointerException
分かるような・・・分からないような・・・


たしかに、nullなんだからNullPointerExceptionな気もします。
でも、ここではSignalのメンバを呼んでいるわけじゃないです。
if文なら signal == Signal.GREEN といった感じで、これならNullPointerExceptionにならないハズ。


・・・。
腑に落ちないので、もう少し調べてみました。
Jadでリバースコンパイル


その結果がこれです(ちと長いですが・・・)。
eclipse 3.4でコンパイルしたものをJad 1.5.8gでリバースコンパイルしました)

    private static final class Signal extends Enum
    {
        (中略)
        public static final Signal GREEN;
        public static final Signal YELLOW;
        public static final Signal RED;
        private static final Signal ENUM$VALUES[];

        static 
        {
            GREEN = new Signal("GREEN", 0);
            YELLOW = new Signal("YELLOW", 1);
            RED = new Signal("RED", 2);
            ENUM$VALUES = (new Signal[] {
                GREEN, YELLOW, RED
            });
        }
        (中略)
    }
public class SwitchTest
{
    (中略)
    private static void CallSignal(Signal signal)
    {
        switch($SWITCH_TABLE$SwitchTest$Signal()[signal.ordinal()])
        {
        case 1: // '\001'
            System.out.println("\u9752\u4FE1\u53F7");
            break;
        case 2: // '\002'
            System.out.println("\u9EC4\u4FE1\u53F7");
            break;
        case 3: // '\003'
            System.out.println("\u8D64\u4FE1\u53F7");
            break;
        default:
            System.out.println("\u7121\u706F\u706B\u30FB\u30FB\u30FB\uFF1F");
            break;
        }
    }

    static int[] $SWITCH_TABLE$SwitchTest$Signal()
    {
        $SWITCH_TABLE$SwitchTest$Signal;
        if($SWITCH_TABLE$SwitchTest$Signal == null) goto _L2; else goto _L1
_L1:
        return;
_L2:
        JVM INSTR pop ;
        int ai[] = new int[Signal.values().length];
        try {
            ai[Signal.GREEN.ordinal()] = 1;
        }
    catch(NoSuchFieldError _ex) { }
        try {
            ai[Signal.RED.ordinal()] = 3;
        }
        catch(NoSuchFieldError _ex) { }
        try {
            ai[Signal.YELLOW.ordinal()] = 2;
        }
        catch(NoSuchFieldError _ex) { }
        return $SWITCH_TABLE$SwitchTest$Signal = ai;
    }

    private static int $SWITCH_TABLE$SwitchTest$Signal[];
}

結構な長さになってました(^^;)
enumはクラスに変換されるというのは知っていましたが、Switch文も大幅に変換されるというのにびっくりです。


この結果から

  • Switch文で列挙型を使った場合、自動で列挙定数の序数を使うように変換される
  • NullPointerExceptionが発生するのは列挙定数の序数を取得するためにordinalメソッドを呼ぼうとしているから

ということが分かりました。


これで納得です。
ちなみに、Switch文で列挙定数の序数を直接使うのではなく、わざわざ変換テーブルを使っている理由などはひしだまさんのページが詳しいです。
Java列挙型メモ(Hishidama's Java enum Memo)
(よく拝見させていただいてますが、この人すごいですね・・・)



Javaは簡単に中が見れるのが便利で面白いですね。

  ∧_∧
 ( ´∀`)< ぬるぽ