Javaのenumにabstractなメソッドを定義してそれぞれの振る舞いを書く

# 概要

このエントリでは、Javaのenumにabstractなメソッドを定義してそれぞれの振る舞いを書くことについて取り扱います。
（EffectiveJava 3rd editionに書いてあるやつ、だいたいそのまんまです。自分でやってみた内容についてメモしておきます。）

# Javaのenum

Javaのenumでは、値だけでなくメソッドを定義することができます。

値だけ定義したパターン

```Java
    enum Operator {
        PLUS, SUBTRACT, MULTIPLY, DIVIDE, NONE
    }
```

これに対し、値ごとにメソッドを定義し、下図のような書き方をすることができます。

```Java
    enum Operator {
        PLUS {
            @Override
            BigDecimal apply(@NonNull BigDecimal lhs, @NonNull BigDecimal rhs) {
                return lhs.add(rhs);
            }
        },
        SUBTRACT{
            @Override
            BigDecimal apply(@NonNull BigDecimal lhs, @NonNull BigDecimal rhs) {
                return lhs.subtract(rhs);
            }
        },
        MULTIPLY {
            @Override
            BigDecimal apply(@NonNull BigDecimal lhs, @NonNull BigDecimal rhs) {
                return lhs.multiply(rhs);
            }
        },
        DIVIDE {
            @Override
            BigDecimal apply(@NonNull BigDecimal lhs, @NonNull BigDecimal rhs) {
                return lhs.divide(rhs, BigDecimal.ROUND_UNNECESSARY);//;
            }
        },
        NONE {
            // allow null for rhs
            @Override
            BigDecimal apply(@NonNull BigDecimal lhs, BigDecimal rhs) {
                return lhs;
            }
        };

        abstract BigDecimal apply(BigDecimal lhs, BigDecimal rhs);
    }
```

enum内の「abstract BigDecimal apply(BigDecimal lhs, BigDecimal rhs);」ですべての値で定義すべきメソッドを指定しておき、それぞれの値の宣言時にメソッドをOverrideしています。


> (補足)上のソースで、「@NonNull」は、[lombokで提供されているアノテーション](https://projectlombok.org/features/NonNull)の一つで、Nullチェックを実装してくれるものです。


# 使用例

前述の形でenumを定義しておくと、例えば以下のようなメソッドが、

```java

    public synchronized BigDecimal pushEvalButton() {
        var v = new BigDecimal(sb.toString());
        switch(currentOperator) {
            case PLUS: {
                v = stack.add(getCurrentValue());
                break;
            }
            case SUBTRACT: {
                  v = stack.subtract(getCurrentValue());
                  break;
            }
            case MULTIPLY: {
                  v = stack.multiply(getCurrentValue());
                  break;
            }
            case DIVIDE: {
                  v = stack.divide(getCurrentValue(), BigDecimal.ROUND_UNNECESSARY);//
                  break;
            }
            case NONE: {
                return v;
            }
            default: {
                throw new RuntimeException("Not defined.");
            }
        }
        currentOperator = Operator.NONE;
        replaceBuffer(v.toPlainString());
        clearStack();
        return v;
    }

```

下記のようにスッキリ書けます。

```java

    public synchronized BigDecimal pushEvalButton() {
        var v = new BigDecimal(sb.toString());

        if(Operator.NONE == currentOperator) {
            return v;
        }
        v = currentOperator.apply(stack, getCurrentValue());
        currentOperator = Operator.NONE;
        replaceBuffer(v.toPlainString());
        clearStack();
        return v;
    }
```

enumのメソッドの方に内容が移動したので元のメソッドからコードが減るのは当然として、以下がメリットと感じました。
- switch内のdefaultを追い出せた
- ほかの場所でもenumのメソッドを使うことにより、処理が一か所にまとめられた

# まとめ

このエントリでは、Javaのenumにabstractなメソッドを定義してそれぞれの振る舞いを書くことについて、例をあげて説明しました。

上記の差分例は、GitHubの[このcommit](https://github.com/hrkt/commandline-calculator/commit/47e7b05075c2bb632b9bdabdf044ab3b5cefdeee)を参照ください。