Enumコード検索は「外から来たコード値を“正しいEnum”に変換する」技
業務システムでは、DBやAPI、設定ファイルなどから「コード値」が文字列や数値で飛んできます。"N" なら新規、"P" なら処理中、"D" なら完了——みたいなやつです。
でもアプリケーション内部では、String のまま扱うより Status.NEW のような Enum にしておいた方が、
型安全で読みやすく、間違いも減ります。
そこで必要になるのが「Enumコード検索」——
「コード値から対応する Enum を探す」ユーティリティです。
前提となる Enum設計:code を持つ Enum を用意する
まずは、コード値を持つ Enum を一つ定義します。
public enum Status {
NEW("N", "新規"),
IN_PROGRESS("P", "処理中"),
DONE("D", "完了");
private final String code;
private final String label;
Status(String code, String label) {
this.code = code;
this.label = label;
}
public String getCode() { return code; }
public String getLabel() { return label; }
}
Javaここでの大事なポイントは、
「外部とやり取りする“コード値”を、Enum のフィールドとして持たせている」ことです。
name()(NEW, IN_PROGRESS, DONE)はあくまで“ソースコード上の識別子”なので、
外部仕様として固定したい値(DBのカラム、APIのパラメータなど)は code に持たせるのが定石です。
一番素朴なコード検索:Stream で values() をなめる
まずは、シンプルに「全要素をなめて一致するものを探す」実装から。
import java.util.Arrays;
public static Status fromCode(String code) {
if (code == null) {
return null;
}
return Arrays.stream(Status.values())
.filter(s -> s.getCode().equals(code))
.findFirst()
.orElse(null);
}
Java使い方の例です。
Status s1 = Status.fromCode("N"); // NEW
Status s2 = Status.fromCode("X"); // null(該当なし)
Javaここで深掘りしたいポイントは三つです。
一つ目は、「Status.values() で全定数を配列として取り出し、Arrays.stream で Stream にしている」ことです。
これで「全候補を順番にチェックする」流れが作れます。
二つ目は、「filter で code が一致するものだけに絞り、findFirst で最初の1件を取っている」ことです。
コード値は一意である前提なので、1件見つかれば十分です。
三つ目は、「見つからなければ null を返している」ことです。
ここは設計の分岐点なので、次でさらに掘ります。
戻り値を Optional にするか、例外にするか、null にするか
コード検索で一番大事なのは、
「該当する Enum が見つからなかったとき、どう振る舞うか」を決めることです。
パターンは大きく三つあります。
Optional を返すパターン
「見つからないのも普通にあり得る」なら、Optional で返すのが素直です。
import java.util.Optional;
public static Optional<Status> findByCode(String code) {
if (code == null) {
return Optional.empty();
}
return Arrays.stream(Status.values())
.filter(s -> s.getCode().equals(code))
.findFirst();
}
Java使い方の例です。
Status s = Status.findByCode("N").orElse(Status.NEW); // デフォルトを決める
Javaここでのポイントは、「該当なし」を Optional.empty() という“正常な結果”として扱えることです。
例外を投げるパターン
「このコード値は必ず Enum に存在しているべき」という前提なら、
見つからなかったときに例外を投げるのもアリです。
public static Status requireByCode(String code) {
return Arrays.stream(Status.values())
.filter(s -> s.getCode().equals(code))
.findFirst()
.orElseThrow(() ->
new IllegalArgumentException("Unknown code: " + code));
}
Javaここでのポイントは、「データ不整合やバグを早めに検知したい」場面で使う、ということです。
null を返すパターン
既存コードとの互換性や、どうしても Optional を使えない事情があるなら、
最初の fromCode のように null を返す選択も現実的です。
ただし、その場合は呼び出し側での null チェックを徹底する必要があります。
パフォーマンスと可読性のための Map キャッシュ
Enum の要素数が少ないうちは values() を毎回なめても問題ありませんが、
呼び出し頻度が高かったり、要素数が多くなってきたりすると、
「コード値→Enum」の Map を事前に作っておく方が効率的です。
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.Arrays;
public enum Status {
NEW("N", "新規"),
IN_PROGRESS("P", "処理中"),
DONE("D", "完了");
private final String code;
private final String label;
private static final Map<String, Status> CODE_MAP =
Arrays.stream(values())
.collect(Collectors.toMap(
Status::getCode,
Function.identity()
));
Status(String code, String label) {
this.code = code;
this.label = label;
}
public String getCode() { return code; }
public String getLabel() { return label; }
public static Status fromCode(String code) {
return CODE_MAP.get(code); // なければ null
}
}
Javaここでの重要ポイントは三つです。
一つ目は、「Enum の static 初期化時に、一度だけ code → Enum の Map を作っている」ことです。
以降の検索は Map#get なので O(1) で高速です。
二つ目は、「Collectors.toMap(Status::getCode, Function.identity()) で、“コード値をキー、Enum 自身を値”とする Map を作っている」ことです。Function.identity() は「そのまま自分自身を返す」関数です。
三つ目は、「コード値が重複していると toMap が例外になる」ことです。
これは逆に、「コード値の一意性を起動時にチェックしてくれる」というメリットにもなります。
汎用化:どの Enum でも使える「コード検索ユーティリティ」
複数の Enum で同じような「code フィールドを持っていて、そこから検索したい」というパターンがあるなら、
インターフェース+ユーティリティで共通化するのもよくあります。
Code を持つ Enum 用インターフェース
public interface CodeEnum {
String getCode();
}
JavaStatus にこれを実装させます。
public enum Status implements CodeEnum {
NEW("N", "新規"),
IN_PROGRESS("P", "処理中"),
DONE("D", "完了");
// 省略
}
Java汎用コード検索ユーティリティ
import java.util.Arrays;
public final class CodeEnums {
private CodeEnums() {}
public static <E extends Enum<E> & CodeEnum> E fromCode(
Class<E> enumType,
String code
) {
if (code == null) {
return null;
}
return Arrays.stream(enumType.getEnumConstants())
.filter(e -> code.equals(e.getCode()))
.findFirst()
.orElse(null);
}
}
Java使い方の例です。
Status s = CodeEnums.fromCode(Status.class, "N");
Javaここでの重要ポイントは二つです。
一つ目は、「<E extends Enum<E> & CodeEnum> という型パラメータで、“Enum かつ CodeEnum を実装している型”に限定している」ことです。
これにより、getCode() が必ず呼べるようになります。
二つ目は、「Enum ごとに同じような fromCode を書かなくて済む」ことです。
共通のルール(null の扱い、該当なしの扱い)を一箇所に集約できます。
まとめ:Enumコード検索で身につけてほしい感覚
Enumコード検索は、
単に「便利メソッドを生やす」話ではなく、
「外部のコード値と内部の Enum を、きちんと結びつける設計」です。
Enum に code フィールドを持たせ、「外部仕様としての値」をそこに集約する。values()+Stream で素朴に検索するところから始め、必要に応じて Map キャッシュに進化させる。
該当なしの扱い(Optional/例外/null)を、業務ルールとしてはっきり決める。
インターフェースや汎用ユーティリティで、「どの Enum も同じパターンでコード検索できる」形に整える。
あなたのコードのどこかに、if ("N".equals(code)) ... else if ("P".equals(code)) ... のような分岐が並んでいる箇所があれば、
それを一度「Enum+コード検索ユーティリティ」に置き換えられないか眺めてみてください。
その小さな整理が、
「コード値と Enum を軸に、外部と内部をきれいに接続できるエンジニア」への、
確かな一歩になります。
