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は簡単に中が見れるのが便利で面白いですね。
∧_∧ ( ´∀`)< ぬるぽ