Strategy enum パターンを使う場合の注意点


普通の enum

普通に enum 定義して、

package foo;

public enum Operation {
    PLUS,
    MINUS,
    ;
}

クラス名を取得すると以下となる。

Operation.PLUS.getClass().toString();
// -> "class foo.Operation"

Operation.PLUS.getClass().getSimpleName();
// -> "Operation"

Operation.PLUS.getClass().getCanonicalName()
// -> "foo.Operation"

enum を画面表示用の文字列に変換する場合のキーとして getSimpleName()enum値のtoString() を使ったりすることはよくあるケース。

Strategy enum パターンにすると

タイプ別で振るまいを変えたい場合には Strategy enum パターン がよく使われる。 TimeUnit なんかと同じですね。

package foo;

public enum Operation {

    PLUS(){
        @Override
        public double apply(double x, double y) {
            return x + y;
        }
    },

    MINUS(){
        @Override
        public double apply(double x, double y) {
            return x - y;
        }
    },
    ;
    // ・・・
    public abstract double apply(double x, double y);
}

当然ながら、各enumは匿名クラスになっているため

Operation.PLUS.getClass().toString();
// -> "class foo.Operation$1"

Operation.PLUS.getClass().getSimpleName();
// -> ""

Operation.PLUS.getClass().getCanonicalName();
// -> null

getSimpleName() は空文字、getCanonicalName() は null となる。

どちらでも同じ結果を得たい場合には

以下のように EnclosingClass で判断できる。

Operation operation = Operation.PLUS;

String name = (operation.getClass().getEnclosingClass() != null)
        ? operation.getClass().getEnclosingClass().getSimpleName()
        : operation.getClass().getSimpleName();

assertThat(name).isEqualTo("Operation");

その他についても同じ。

Operation operation = Operation.PLUS;
Class<?> enclosingClass = operation.getClass().getEnclosingClass();

if (enclosingClass != null) {

    assertThat(enclosingClass.toString())
        .isEqualTo("class foo.Operation");

    assertThat(enclosingClass.getSimpleName())
        .isEqualTo("Operation");

    assertThat(enclosingClass.getCanonicalName())
        .isEqualTo("foo.Operation");

} else {

   assertThat(operation.getClass().toString())
        .isEqualTo("class foo.Operation");

    assertThat(operation.getClass().getSimpleName())
        .isEqualTo("Operation");

    assertThat(operation.getClass().getCanonicalName())
        .isEqualTo("foo.Operation");

}


過去3回ぐらい遭遇した。 注意しましょう。