-- Views
January 10, 26
スライド概要
BuriKaigi 2026 登壇資料
『Enum、お前は一体何者だ?複数言語で見る「列挙型」の多様な進化と使い方』
Ruby/Javaプログラマー。エンジニアリングマネージャー。 軽度の広く浅いオタク。
Enum、お前は一体何者だ? 複数言語で見る「列挙型」の多様な進化と使い方
登壇者、お前は一体何者だ? 河野 裕隆(こうの) ▹ 📛 X: @hk_it7 ▹ 🏢 虎の穴ラボ株式会社 でチームリーダー ▹ ☕ ECサイト開発でJavaメインに活動 ▹ 💎 ファンクラブ支援サイトをRuby on Railsで開発 ▹ 🎤 地域エンジニアコミュニティ、東葛.devを主催
今日のアジェンダ public enum TodaysAgenda { WHAT_IS_ENUM("Enumとは", "定数グループ化、型安全性、保守性"), JAVA("クラスインスタンス", "振る舞いのカプセル化、メソッド定義"), CSHARP("整数定数", "シンプルさ、ビットフラグ、拡張メソッド"), PHP("Backed Enum", "モダン設計、DB連携、後発の利点"), TYPESCRIPT("型システム", "ランタイムとのギャップ、as const推奨"), RAILS("フレームワーク機能", "ActiveRecord統合、DB整合性"); } TodaysAgenda.WHAT_IS_ENUM.getDescription(); →
Enumとは? 基本概念 定義: 有限の値の集合を表現するデータ型 用途: 状態、カテゴリ、オプション、モードなどの表現 注文ステータス ┌─────────────┐ │ PENDING │ │ PROCESSING │ │ SHIPPED │ DELIVERED │ │ └─────────────┘
話そうと思った動機
私の背景 Javaメインのエンジニア 業務では主にJavaを使用 Railsとの出会い 別プロジェクトにアサインされ、Ruby on Railsを使うことに → RailsのEnumとJavaのEnumが全然違う! 「同じ"Enum"なのに、なぜこんなに違うんだろう?」という疑問がずっとあった
TSKaigi 2025での気づき yamanokuさんの発表 「TypeScriptのEnumについて」の発表を聴講 学んだこと ▹ JSにはそもそもEnumがない ▹ TypeScriptのEnumはあまり使われていない ▹ Node.js 25のType Strippingで状況が変化 「TypeScriptのEnumって、JavaやRailsとはまた 違うアプローチなんだ…」
PHPのEnumとの出会い tyamahoriさんとの出会い 文脈: 地域のエンジニアコミュニティで一緒に活動 → コミュニティの勉強会で技術の話をする仲 Zennの記事を発見 PHPのEnumについての記事 PHP Enumは複数のメンバーを持てない この違いを整理して話したい!
なぜEnumなのか? 共通概念 - Enumは全言語に共通する概念 設計思想の違い - しかし実装は全く異なる 型安全性 - この違いから各言語の思想が見える
Enumがない世界の問題 // 定数での実装の問題 public static final int STATUS_DRAFT = 0; public static final int STATUS_PUBLISHED = 1; public static final int STATUS_ARCHIVED = 2; // 問題1: 型安全性がない public void setStatus(int status) { // 999を渡されても防げない } // 問題2: 可読性が低い if (status == 1) { // 1って何? } // 問題3: リファクタリングが困難 問題点 🚫 型安全性の欠如 不正な値の代入を防げない 🚫 可読性の低下 コード上で意味が不明確 🚫 保守性の問題 使用箇所の特定が困難
Enumの4つの役割 🛡️ 型安全性の確保 📖 可読性の向上 不正な値の代入を防ぐ コード上で意味が明確 🔧 保守性の向上 💡 IDEサポート 値の追加・変更が容易 自動補完、リファクタリング
Enumの歴史:導入時期の比較 言語/FW バージョン リリース年 特徴 C# 1.0 2002年 初期から存在。整数ベース Java JDK 5.0 2004年 Typesafe Enumパターン→言語機能化 TypeScript 0.9 2013年 Numeric enum。後にString enum追加 Rails 4.1 2014年 ActiveRecord::Enumとして導入 PHP 8.1 2021年 Pure/Backed Enum。最も新しい
Enumはクラスか定数か JavaとC#とPHPでの比較
Java - クラスインスタンス
各Enum定数が振る舞いを持つ
public enum PaymentMethod {
CREDIT_CARD {
public int calculateFee(int amount) {
return (int) (amount * 0.03);
}
},
BANK_TRANSFER {
public int calculateFee(int amount) {
return 200; // 固定200円
}
};
}
public abstract int calculateFee(int amount);
特徴
✅ フィールド複数可能 各定数に複数のフィールドを
持てる
✅ メソッド定義可能 抽象メソッドで振る舞いを強制
✅ ポリモーフィズム 各定数が異なる実装を持てる
⚠️ メモリ効率: ○ クラスインスタンスのためやや重
い
C# - 整数定数
シンプルかつ効率的なアプローチ
// Flags属性でビットフラグを有効化
[Flags]
public enum Permission
{
None = 0,
// 0000
Read = 1,
// 0001
Write = 2,
// 0010
Delete = 4
// 0100
}
// ビット演算で組み合わせ
var perm = Permission.Read
| Permission.Write;
特徴
✅ 整数のみ 内部的には整数として扱われる
✅ ビットフラグ 複数の値を組み合わせ可能
✅ 拡張メソッド Enum自体を変更せず機能追加
✅ メモリ効率: ◎ 整数定数のため非常に軽量
Java vs C# 比較 観点 JAVA C# 本質 クラスインスタンス 整数定数 メソッド 各定数で実装可能 拡張メソッドで追加 メモリ やや重い 軽量 ビット演算 不可 Flags属性で簡単 ポリモーフィズム ◎ △(拡張メソッド) 型安全性 ◎ △(整数を受け入れる)
設計思想の違い Java: OOPの原則に忠実 C#: シンプルさと効率性 振る舞いのカプセル化を重視 実用性と拡張性のバランス ▹ Enumは単なる定数ではなく「型」 ▹ 基本はシンプルな整数定数 ▹ 各定数がオブジェクトとして振る舞いを持つ ▹ ビットフラグで複雑な状態管理 ▹ ポリモーフィズムにより柔軟な設計 ▹ 拡張メソッドで後から機能追加 ▹ 哲学: 正しいOOP設計を追求 ▹ 哲学: 実用性とパフォーマンス重視 どちらが優れているということではなく、アプローチの違い
Java - 振る舞いのカプセル化
各Enum定数で異なるロジックを実装
public enum PaymentMethod {
CREDIT_CARD("クレジットカード") {
public int calculateFee(int amount) {
return (int) (amount * 0.03); // 3%
}
},
BANK_TRANSFER("銀行振込") {
public int calculateFee(int amount) {
return 200; // 固定200円
}
};
private final String displayName;
}
public abstract int calculateFee(int amount);
使用例: switchが不要!
int fee = method.calculateFee(15000);
Java switch式の網羅性チェック
◎ switch式(推奨)
△ switch文 + default
// 網羅性チェックあり
int fee = switch (method) {
case CREDIT_CARD ->
(int) (amount * 0.03);
case BANK_TRANSFER -> 200;
case CASH_ON_DELIVERY -> 300;
// 新しいEnum値を追加したら
// コンパイルエラーで検知
};
// 網羅性チェックなし
switch (method) {
case CREDIT_CARD:
fee = (int) (amount * 0.03);
break;
case BANK_TRANSFER:
fee = 200;
break;
default:
// 新しいEnum値を追加しても
// 気づけない
fee = 0;
}
推奨: Javaでswitch/caseを使う場合は、switch式を使い、default節を避ける
C# - シンプルさと拡張性
ビットフラグと拡張メソッド
// Flags属性でビットフラグを有効化
[Flags]
public enum Permission
{
None = 0,
// 0000
Read = 1,
// 0001
Write = 2,
// 0010
Delete = 4, // 0100
Admin = 8
// 1000
}
// 組み合わせ
var perm = Permission.Read
| Permission.Write;
// 拡張メソッドで機能追加
public static class PermissionExt
{
public static string GetName(
this Permission p)
{
return p switch {
Permission.Read => "読取",
Permission.Write => "書込",
_ => "不明"
};
}
}
// 使用
perm.GetName(); // "読取"
PHP - 良いとこ取りのモダンEnum
文字列バッキング値 + メソッド定義
enum OrderStatus: string
{
case PENDING = 'pending';
case PROCESSING = 'processing';
case SHIPPED = 'shipped';
}
// メソッドを定義可能
public function canCancel(): bool
{
return match($this) {
self::PENDING, self::PROCESSING => true,
default => false,
};
}
DB連携が簡単: $status->value で "pending" を取得
JavaとC#とPHPの比較表 観点 JAVA C# PHP 本質 クラス 整数定数 言語機能 フィールド 複数可能 整数のみ 1つのみ メソッド ○ 拡張メソッド ○ ポリモーフィズム ◎ × △ match式 ビットフラグ △ ◎ Flags △ DB連携 ○ △ ◎ 最高 メモリ効率 ○ ◎ ○
Java、C#、PHPの設計思想 Java C# PHP OOPの原則に忠実 シンプルさと効率性 Web開発の実用性 ▹ 振る舞いのカプセル化 ▹ 基本はシンプルな整数 ▹ DB連携を最優先 ▹ 型安全性を最重視 ▹ ビットフラグで柔軟性 ▹ 後発の利点を活用 ▹ ポリモーフィズム重視 ▹ 拡張メソッドで機能追加 ▹ 実用的なモダン設計 どれが優れているかではなく、要件に合わせて選択することが重要
ネイティブでない言語のアプロ ーチ TypeScriptとRuby on Railsの工夫
TypeScript - 型システムのEnum
数値Enum
文字列Enum
enum HttpStatus {
OK = 200,
BadRequest = 400,
NotFound = 404,
}
enum LogLevel {
DEBUG = "DEBUG",
INFO = "INFO",
WARN = "WARN",
ERROR = "ERROR",
}
function handleResponse(status: HttpStatus) {
// ...
}
handleResponse(HttpStatus.OK); // OK
handleResponse(200); // OK(危険!)
handleResponse(999); // OK(危険!)
function log(level: LogLevel, message: string) {
console.log(`[${level}] ${message}`);
}
log(LogLevel.INFO, "処理開始"); // OK
// log("INFO", "処理開始"); // エラー
TypeScript Enumの問題点
問題1: 型安全性が危うい
// 数値Enumは数値を直接渡せてしまう
handleResponse(999); // コンパイルエラーにならない!
問題2: リバースマッピングで肥大化
// トランスパイル後のJavaScript
{
OK: 200,
NotFound: 404,
200: "OK",
// リバースマッピング(不要)
404: "NotFound"
// リバースマッピング(不要)
}
問題3: Tree-shakingが効きにくい
TypeScript 推奨アプローチ
❌ Enum(非推奨)
✅ const object(推奨)
enum LogLevel {
DEBUG = "DEBUG",
INFO = "INFO",
}
const LogLevel = {
DEBUG: "DEBUG",
INFO: "INFO"
} as const;
問題点:
type LogLevel = typeof LogLevel[
keyof typeof LogLevel
];
▹ Tree-shakingが効かない
▹ リバースマッピング
▹ バンドルサイズ増加
function log(
level: LogLevel,
message: string
) { ... }
log(LogLevel.INFO, "OK"); // OK
log("INVALID", "NG"); // エラー!
Node.js 25の衝撃 Type Stripping機能の導入 Node.js 25 (2025年10月リリース) でType Stripping機能が安定機能として導入 # Node.js 25でTypeScriptを直接実行 node app.ts ❌ 重大な問題: Enumは動かない Type Strippingは型を削除するだけで、コード変換は行わない => "enum" キーワードを削除できない SyntaxError: Unexpected token 'enum'
Node.js 25での正解
機能
TYPE STRIPPING
理由
✅ as const
動作する
純粋なJavaScript
❌ enum
動作しない
コード生成が必要
❌ const enum
動作しない
インライン展開が必要
✅ 唯一の正解: as const
const Status = {
Active: "active",
Inactive: "inactive",
} as const;
type Status = (typeof Status)[keyof typeof Status]; // 型定義は削除されるが、ランタイムには影響しない
Ruby on Rails - フレームワーク主導
規約ベースで多くの機能を自動生成
モデル定義
自動生成されるメソッド
class Article < ApplicationRecord
enum status: {
draft: 0,
published: 1,
archived: 2
}
end
# 状態チェック
article.draft?
article.published?
# => true/false
# => true/false
# 状態変更
article.published!
# statusを1に設定
# スコープ
Article.draft
Article.published
# WHERE status = 0
# WHERE status = 1
# 一覧
Article.statuses
# => {"draft"=>0, ...}
Rails 文字列enum推奨 整数enum(レガシー) 文字列enum(推奨) enum status: { draft: 0, published: 1 } enum status: { pending: 'pending', processing: 'processing' } 問題点: 利点: ▹ DB: status = 0 (何?) ▹ DB: status = 'pending' (明確!) ▹ 可読性が低い ▹ 可読性が高い ▹ 順序変更で破損 ▹ 順序変更に強い Rails 7+: 文字列enumが推奨
TypeScriptとRailsの対比 TypeScript Rails ▹ 静的型付け ▹ 動的型付け ▹ コンパイル時チェック ▹ ランタイムチェック ▹ 型安全性優先 ▹ 生産性優先 ▹ const object推奨 ▹ 規約による自動生成 ▹ Node.js 25で制約増加 ▹ 変わらず高生産性 両極端のアプローチ - それぞれの強みを理解することが重要
まとめ
本日の学び 1. Enumは単なる定数ではな い 各言語の設計思想が凝縮されている 2. 型安全性の重要性 Enumは型安全性を実現する強力な手段 3. 後発の利点を活かした設計 PHPはRailsの実用性とJavaの表現力を併せ持つ 4. 他言語を知ることの価値 自分のメイン言語の強みと制約を再認識できる
Enumは単なる定数ではない 各 言語の設計 思想が凝縮さ れた、 型安 全性 と保守性を 実現する強力 な手段である 他言語のEnumを知ることで、 あなたのコーディングに新たな視点と熱意を ご清聴ありがとうございました