Enum 徹底入門

488 Views

May 30, 26

スライド概要

JJUG CCC 2026 Spring / こうの(虎の穴ラボ) @hk_it7 登壇資料

profile-image

Ruby/Javaプログラマー。エンジニアリングマネージャー。 軽度の広く浅いオタク。

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

ダウンロード

関連スライド

各ページのテキスト
1.

Enum 徹底入門 Javaの「クラス」としての Enum を使い倒す JJUG CCC 2026 Spring / こうの(虎の穴ラボ)

2.

リンク Twitter 実況補助ツール サンプルコード & スライド HTML https://h-kono-it.github.io/conference-tweet-helper/ https://github.com/h-kono-it/jjug_ccc_2026_spring-enum 実況補助ツールは今すぐ、手元で見たい方は右の QR からどうぞ 2

3.

早速ですが…「定数」の管理、どうしてる? public class OrderService { public static final int STATUS_PENDING = 0; public static final int STATUS_PROCESSING = 1; public static final int STATUS_COMPLETED = 2; public static final int STATUS_FAILED = 3; public void process(int status) { if (status == STATUS_PENDING) { // ... } } } シンプルに見えるが、落とし穴がある 3

4.

どんな問題が起きる? // 定数を「使ってくれる」保証がない service.process(STATUS_PENDING); // ◯ お行儀の良い呼び出し service.process(0); // ✗ 直書きされても止められない service.process(999); // ✗ 範囲外の値もコンパイルは通る // String 版も同じ service.process("pedning"); // ✗ typo がすり抜ける service.process("PENDING"); // ✗ 大文字小文字のゆれも通る 強制力がない — 「定数を使う」のは規約頼み。直書きを防げない 範囲外の値を常に考慮 — テストで「不正値が来たら」のケースが付きまとう 意図が伝わらない — 引数の / だけでは何を渡すべきか分からない int String これを「型」で解決するのが Enum。今日はその活用術を詳しく話します! 4

5.

今日話すこと 1. なぜ Enum か? — 動機とメリット 2. Enum の仕組み — JLS とコンパイル後の姿 3. プロパティと振る舞いを持つ Enum 4. Enum と DB — ORM マッピング 5. Enum の活用パターン — EnumSet/EnumMap・状態遷移・Singleton 6. Enum の進化 — switch 式 7. Enum vs Sealed Class — パターンマッチングとの相性 5

6.

Enumの「端的で強力な」メリット enum OrderStatus { PENDING, PROCESSING, COMPLETED, FAILED } public void process(OrderStatus status) { ... } // 有効な値しか渡せない service.process(OrderStatus.PENDING); // ✓ IDE が補完してくれる service.process(999); // ✗ コンパイルエラー service.process("pedning"); // ✗ コンパイルエラー 定数の意味・有効な範囲が 型として明示 される 6

7.

型で実装を制限し、仕様を明確にする // シグネチャだけで「渡せる値」が伝わる void sendNotification(NotificationType type, Priority priority); order.setStatus( /* ここで補完 */ ) // → IDE も Copilot / Claude も OrderStatus の定数一覧を提示 メソッドシグネチャを見るだけで 何を渡せるか が分かる IDE 補完で有効な値の一覧が即座に出る/AI も型情報を読んで生成 する AI が生成したコードも コンパイラが検証 してくれる 選択肢を増やしたときの対処漏れも検出できる(後述の switch 網羅性) 意図をコードに込めるほど、人間も AI も読みやすいコードになる 7

8.

Enum の仕組み JLS の定義とコンパイル後の姿 8

9.

Enum の仕組み(JLS の定義) Java の Enum は 特殊なクラス が自動的に付与される(明示的には書けない) 各定数は なシングルトンインスタンス コンストラクタは暗黙的に / はコンパイラが自動生成 extends Enum<T> static final private values() valueOf() Season s = Season.SPRING; s.name() // → "SPRING" Season.values() // → Season[] 全定数の配列 Season.valueOf("AUTUMN") // → Season.AUTUMN 9

10.
[beta]
コンパイル後の姿
// javac が生成するイメージ
public final class Season extends Enum<Season> {
public static final Season SPRING = new Season("SPRING", 0);
public static final Season SUMMER = new Season("SUMMER", 1);
// AUTUMN, WINTER も同様...
private Season(String name, int ordinal) {
super(name, ordinal);
}
// values(), valueOf() も自動生成される
}

クラスは
→ 継承不可
各定数はシングルトン → での比較が安全
final

==

10

11.

プロパティと振る舞いを持つ Enum 11

12.

フィールドを持つ Enum public enum Status { PENDING("pending", "処理待ち"), PROCESSING("processing", "処理中"), COMPLETED("completed", "完了"), FAILED("failed", "失敗"); 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; } } コンストラクタは暗黙的に private 。フィールドは final にするのが慣習。 12

13.
[beta]
static ファクトリメソッド
// DB の値(code 文字列)から Enum を逆引き
public static Status fromCode(String code) {
return Arrays.stream(values())
.filter(s -> s.code.equals(code))
.findFirst()
.orElseThrow(() ->
new IllegalArgumentException("不正なコード: " + code));
}
// 見つからない場合は Optional で返す版
public static Optional<Status> findByCode(String code) {
return Arrays.stream(values())
.filter(s -> s.code.equals(code))
.findFirst();
}

valueOf()

は定数名での逆引き。コード値での逆引きは自前で実装する。
13

14.

振る舞いのポリモーフィズム(抽象メソッド) public enum Operation { ADD { @Override public double apply(double x, double y) { return x + y; } }, SUBTRACT { @Override public double apply(double x, double y) { return x - y; } }; // MULTIPLY, DIVIDE も同様... public abstract double apply(double x, double y); } // 使う側は switch 不要 double result = Operation.ADD.apply(3.0, 4.0); // → 7.0 Strategy パターン を Enum の中にコンパクトに閉じ込められる 14

15.
[beta]
Lambda フィールド(よりコンパクトに)
public enum Operation {
ADD("+",

(x, y) -> x + y),

SUBTRACT("-", (x, y) -> x - y),
MULTIPLY("*", (x, y) -> x * y),
DIVIDE("/",

(x, y) -> x / y),

POWER("^",

Math::pow);

// メソッド参照も OK

private final String symbol;
private final BiFunction<Double, Double, Double> fn;
Operation(String symbol, BiFunction<Double, Double, Double> fn) {
this.symbol = symbol; this.fn = fn;
}
public double apply(double x, double y) { return fn.apply(x, y); }
}

抽象メソッド版より 定義がコンパクト。定数クラス体が不要になる。
15

16.

Enum と DB ORM マッピングのベストプラクティス 16

17.

@Enumerated @Enumerated だけだと足りない理由 は JPA 標準の Enum カラム マッピング指定。 ORDINAL と STRING の2択。 // ORDINAL — 宣言順の番号(0, 1, 2...)をそのまま保存 @Enumerated(EnumType.ORDINAL) private Status status; // → 定数の追加・並び替えで既存データが壊れる // STRING — 定数名("PENDING", "COMPLETED"...)をそのまま保存 @Enumerated(EnumType.STRING) private Status status; // → 定数をリネームすると既存データとズレる Java のコード と DB が密結合 => Java 側を修正しづらくなる コード表現と データ管理を分離する ために、Enum 自身にコード値を持たせる 17

18.
[beta]
DBにはコード値を持たせる
enum Status {
PENDING("pending"), COMPLETED("completed"), FAILED("failed");
private final String code;
Status(String code) { this.code = code; }
public String getCode() { return code; }
public static Status fromCode(String code) {
return Arrays.stream(values())
.filter(s -> s.code.equals(code))
.findFirst().orElseThrow();
}
}

Java の定数名(

PENDING

)と DB の値(

"pending"

)を 切り離す
18

19.
[beta]
Converter で橋渡し(JPA)

Spring Data JPA / Hibernate で使える
MyBatis の場合は
で同様のことができる
定数のリネームが DB に影響しない

AttributeConverter

TypeHandler

@Converter(autoApply = true)
class StatusConverter
implements AttributeConverter<Status, String> {
@Override
public String convertToDatabaseColumn(Status s) {
return s.getCode();

// Java → DB

}
@Override
public Status convertToEntityAttribute(String code) {
return Status.fromCode(code); // DB → Java
}
}

19

20.

Enum の活用パターン EnumSet / EnumMap・状態遷移・Singleton 20

21.
[beta]
集合と判定に便利な EnumSet
enum Permission { READ, WRITE, DELETE, ADMIN }
EnumSet<Permission> userPerms = EnumSet.of(Permission.READ, Permission.WRITE);
EnumSet<Permission> allPerms

= EnumSet.allOf(Permission.class);

EnumSet<Permission> restricted = EnumSet.complementOf(userPerms); // [DELETE, ADMIN]
EnumSet<Day> weekdays = EnumSet.range(Day.MON, Day.FRI);

// 範囲指定

boolean canDelete = userPerms.contains(Permission.DELETE);

// false

内部実装は ビットベクター →
/
/
/
権限・フラグの集合 に便利
of

allOf

complementOf

より高速・省メモリ
など生成 API が豊富

HashSet
range

21

22.
[beta]
集計に便利な EnumMap
// 注文ステータス別の件数を集計
EnumMap<OrderStatus, Integer> count = new EnumMap<>(OrderStatus.class);
for (OrderStatus s : OrderStatus.values()) count.put(s, 0);

// 全キーを 0 で初期化

orders.forEach(o -> count.merge(o.getStatus(), 1, Integer::sum));
count.forEach((status, n) -> System.out.printf("%-12s: %d件%n", status, n));
// PENDING

: 12件

// PROCESSING

:

// COMPLETED

: 47件

// FAILED

:

3件
0件

← 0 件のステータスも漏れなく出る

全キーを事前初期化 できるので「0件」も含めて確実にレポートできる
順序は Enum の宣言順 で安定 → 表・グラフの並びがブレない
内部は 配列 実装 →
より高速・省メモリ
HashMap

ダッシュボード・帳票・メトリクス集計で刺さる

22

23.
[beta]
ステートマシンを Enum で表現
public enum DocumentState {
DRAFT

{ public Set<DocumentState> nextStates() { return Set.of(REVIEWING, CANCELLED); } },

REVIEWING { public Set<DocumentState> nextStates() { return Set.of(APPROVED, REJECTED, DRAFT); } },
APPROVED

{ public Set<DocumentState> nextStates() { return Set.of(PUBLISHED, CANCELLED); } },

PUBLISHED { public Set<DocumentState> nextStates() { return Set.of(ARCHIVED); } },
ARCHIVED

{ public Set<DocumentState> nextStates() { return Set.of(); } };

// 終端

public abstract Set<DocumentState> nextStates();
public DocumentState transitionTo(DocumentState next) {
if (!nextStates().contains(next))
throw new IllegalStateException(this + " → " + next + " は許可されていない");
return next;
}
}

状態と遷移ルールが Enum 1つに閉じる — 仕様書のようなコード
23

24.

Enum で Singleton(Effective Java Item 3) public enum AppConfig { INSTANCE; // ← これだけで Singleton 完成 private int requestCount = 0; public void handleRequest(String msg) { requestCount++; /* ... */ } public int getRequestCount() { return requestCount; } } // 使う側 AppConfig.INSTANCE.handleRequest("hello"); クラスロード時に1回だけ初期化 → スレッドセーフ シリアライズ しても同一インスタンスが保たれる リフレクション攻撃 にも強い Effective Java 推奨。 private static final + ロックを書く必要なし 24

25.

Enum の進化 switch 式の網羅性 25

26.
[beta]
switch 式の網羅性チェック(Java 14+)
String label = switch (status) {
case PENDING

-> "処理待ち";

case PROCESSING -> "処理中";
case COMPLETED

-> "完了";

case FAILED

-> "失敗";

// 全ケース網羅 → default 不要!
};

新しい定数を追加するとコンパイルエラーになる
error: the switch expression does not cover all possible input values

対処漏れをゼロに。

26

27.

Enum vs Sealed Class それぞれの使いどき 27

28.

Enum vs Sealed Class Enum Sealed Class / Interface インスタンス数 コンパイル時確定(固定) 実行時に複数作れる データの形 全定数で同じ型のフィールド サブクラスごとに異なる構造 switch 網羅性 ✓(Java 14+) ✓(Java 21+) シリアライズ 自動的に安全 要実装 使いどき 値の種類・状態が固定 形が異なるデータ // Enum が向く:「状態の種類」 enum OrderStatus { PENDING, SHIPPED, DELIVERED, CANCELLED } // Sealed が向く:「形が違うデータ」 sealed interface Shape permits Circle, Rectangle, Triangle {} 28

29.
[beta]
Sealed × パターンマッチング(Java 21+)
sealed interface Shape permits Circle, Rectangle, Triangle {}
record Circle(double r) implements Shape {}
record Rectangle(double w, double h) implements Shape {}
record Triangle(double a, double b, double c) implements Shape {}
static double area(Shape shape) {
return switch (shape) {
case Circle c

-> Math.PI * c.r() * c.r();

case Rectangle r -> r.w() * r.h();
case Triangle t

-> heron(t.a(), t.b(), t.c());

// sealed なので全ケース網羅 → default 不要!
};
}

型パターン で各サブクラスのフィールドにそのままアクセスできる
Enum の switch 網羅性が 「形の違うデータ」にまで拡張 されたイメージ
Enum を sealed interface の実装にも混ぜられる(特殊な単一定数など)

29

30.

今日のまとめ! 1. Enum は「クラス」— フィールドと振る舞いを持たせろ コード値・ラベル・ロジックを Enum 1つに閉じ込める。Strategy も状態遷移もここで完結す る。 2. DB と Enum の間には Converter を挟め は使わない。Java の定数名と DB の値を切り離し、リネームの自由を確保する。 3. switch 式の網羅性で「対処漏れ」をコンパイル時に検出 を書かない勇気。定数追加 → 全 switch がコンパイルエラーで教えてくれる。 @Enumerated default Enum を「なんとなく」使う段階を卒業して、Java の型システムを最大限に活かしましょう! 30

31.

ありがとうございました 名前: こうの 所属: 虎の穴ラボ テックリード Twitter: @hk_it7 31

32.

サンプルコード & リンク サンプルコード Twitter 実況補助ツール https://github.com/h-kono-it/jjug_ccc_2026_spring-enum https://h-kono-it.github.io/conference-tweet-helper/ 32

33.

付録:用語集 当日は口頭で補足した用語のまとめ 33

34.

用語集(言語仕様・機能) メソッドシグネチャ — メソッドの「名前・引数の型」の組み合わせ。戻り値の型はシグネチャ に含まれない(JLS の定義)。 のような宣言部 分のこと。 JLS(Java Language Specification) — Java の言語仕様を定めた公式文書。Enum の動 作もここで規定されている。 switch 式(switch expression) — Java 14 で導入。 構文で値を返せる。switch 文 (statement)は値を返さず、 が必要だった。 Sealed Class / Interface — Java 17 で導入。 で実装・継承できるクラスを明示的 に制限できる。Java 21 以降のパターンマッチングと組み合わせると網羅性チェックが効く。 網羅性チェック(Exhaustiveness Check) — switch 式で全ケースが処理されているかをコ ンパイラが検証する仕組み。Enum・Sealed Class で有効になる。 List<Order> findByStatus(OrderStatus status) -> break permits 34

35.

用語集(設計パターン・ORM) シングルトン(Singleton) — JVM 内にインスタンスが1つだけ存在することを保証するパタ ーン。Enum の各定数はすべてシングルトン。 Strategy パターン — 処理のアルゴリズムを切り替え可能にするデザインパターン。Enum の 抽象メソッドや Lambda フィールドで実現できる。 ポリモーフィズム(Polymorphism) — 同じ型・インターフェースで異なる振る舞いを実現す る OOP の概念。Enum の抽象メソッドや interface 実装で活用できる。 AttributeConverter — JPA(Spring Data JPA / Hibernate)で型変換を定義するインター フェース。Enum DB カラムの変換に使う。 TypeHandler — MyBatis で型変換を定義する仕組み。JPA の AttributeConverter に相当す る役割。 35