わくわく技術ブログ

プログラミング・統計・機械学習

Javaのenumとswitchとコンパイル結果

目的

Oracleの配布しているJDKと、Eclipseで使われるJDKで、コンパイル後のファイル数が違うケースに気づいたのでメモ。
switch文でenumを使う時のコンパイル結果についてです。

本題

Javaenumが、ただの定数ではなくて特殊なクラスとして扱われるのは有名なことかと思います。EffectiveJava(読みかけ 汗)にも書いてありました。
クラスなので、コンパイルすればenum定義は一つの.classファイルになります。クラス内で定義したら、staticな内部クラスとして、やっぱり別の.classになります。

ところで、そんなenumの利点として、switch文の条件にできるという点があります。
では、このようなコードはコンパイルするとどういったファイル名になるでしょうか?

Person.java

public class Person {
    public void work(Day day) {
        switch (day) {
        case SAT:
            System.out.println("寝て過ごす");
            break;
        case SUN:
            System.out.println("遊びに出かける");
            break;
        default:
            System.out.println("働く");
            break;
        }
    }
}

Day.java

public enum Day {
    MON, TUE, WED, THU, FRI, SAT, SUN
}

Eclipseコンパイル結果は予想通り

自分は、当然Person.classとDay.classの2つになると思ってました。
そして、Eclipseを使うとその通りになります。(バージョンは4.2と4.5で確認)

Oracle JDKでは違った

でも、OracleJDK(1.7)のjavacを使ったら、
* Day.class * Person.class * Person$1.class の3つになりました。なぜだ(・д・)

javap Person$1.class とすると、

Compiled from "Person.java"
class Person$1 {
  static final int[] $SwitchMap$Day;
  static {};
}

だそうな???

検索すると次のような記事に当たります

enum 定数の switch 文の実装 - happynowの日記

Java列挙型メモ(Hishidama's Java enum Memo)

enumの値が変わったり増減したりしても、enumを使う側はコンパイルし直さなくても済むというのがenumの特徴です。そういった挙動を既存の文法だけで再表現するための実装として、Oracleのjavacでは内部的に勝手にクラスを作っていたんですね。そしてEclipseコンパイラでは別の実装になっていると。

感想

Javaではクラスごとに.classファイルが作られることになっています。
しかも無名のクラスは$1のような通し番号のついたクラスになったりします。

実装依存なのだとしても、コンパイル後のファイル数まで違ってしまうのはなんとも気持ち悪いというか。
たとえば1つの.javaファイルから100個の.classが作られることも許されているのかなみたいな。
コンパイル後のことなんて気にするなと言われれば、まぁそうなんですが...('_')ナンダカナー

全然別の話題ですが

またオーボエはじめました。