定数クラスの全体像
定数クラスは「アプリ全体で共有する変わらない値」を一か所にまとめて、意味のある名前で再利用するための入れ物です。マジックナンバーや散らばった文字列を排除し、変更箇所を一か所に集約できます。基本は「public static final」で公開し、クラスはインスタンス化不可(private コンストラクタ)にして名前空間として使います。関連が強いものは列挙型(enum)を優先し、単なる値の束は定数クラスへ——これが実用的な使い分けです。
基本形と安全な作り方
最小の定数クラス(インスタンス化不可)
public final class AppConst {
private AppConst() {} // インスタンス化禁止
public static final int MAX_RETRY = 3;
public static final String DEFAULT_USER = "guest";
public static final double TAX_RATE = 0.10;
}
Java- インスタンス化を防ぐための private コンストラクタは「定数クラスの型」です。
- フィールドは public static final(不変)。複合オブジェクトは不変な型(List.of、Map.of など)で持つと安全です。
不変コレクションを持つ
import java.util.List;
import java.util.Map;
public final class Roles {
private Roles() {}
public static final List<String> DEFAULT_ROLES = List.of("USER");
public static final Map<String, Integer> PERMISSIONS = Map.of("READ", 1, "WRITE", 2);
}
Java- 不変ならスレッドセーフで、思わぬ変更が起きません。
名前空間の切り方(関連ごとに分ける)
分類してスッキリ保つ
public final class PathsConst {
private PathsConst() {}
public static final String TMP_DIR = "/tmp/app";
public static final String DATA_DIR = "/var/app/data";
}
public final class MsgConst {
private MsgConst() {}
public static final String ERR_NOT_FOUND = "not_found";
public static final String ERR_UNAUTHORIZED = "unauthorized";
}
public final class NumbersConst {
private NumbersConst() {}
public static final int PAGE_SIZE_DEFAULT = 50;
public static final int PAGE_SIZE_MAX = 200;
}
Java- 1クラスに詰め込みすぎず、目的別に分けると見通しが良くなります。
- 変更時も「どこを見るか」が明確です。
重要ポイントの深掘り:enum との使い分け
値に意味と集合があるなら enum
public enum UserRole {
ADMIN, EDITOR, VIEWER
}
Java- 取りうる値の集合が固定なら enum が最適です。型安全で、switch・比較が明瞭になります。
生の値だけなら定数クラス
- 単なる閾値、文言キー、パス、正規表現などは定数クラスが軽量で扱いやすいです。
付加情報を持つ enum
public enum Currency {
JPY("¥"), USD("$"), EUR("€");
public final String symbol;
Currency(String symbol) { this.symbol = symbol; }
}
Java- enum はフィールドやメソッドも持てるため、「定数+意味」のまとまりを型で表現できます。
アンチパターンと注意点
Constant Interface は避ける
// 悪例:インターフェース継承で定数をばら撒く
public interface BadConst {
int MAX = 5;
}
public class UseIt implements BadConst { /* どこでも見えてしまう */ }
Java- 継承は「振る舞いの契約」のための仕組み。定数のために使うと依存が広がり、意図しない露出になります。定数クラスで参照する形が安全です。
可変オブジェクトを公開しない
// 悪例:mutable を公開
public static final java.util.List<String> ITEMS = new java.util.ArrayList<>();
// → どこからでも変更できて壊れる
Java- 不変で公開(List.of / Collections.unmodifiableList)にするか、防御的コピーを返します。
static import の使い方
import static com.example.AppConst.MAX_RETRY;
if (retries > MAX_RETRY) { /* ... */ }
Java- 使い過ぎると「どこから来た値か」が不明瞭になります。よく知られた少数だけに限定すると読みやすさを保てます。
実用例で身につける
例 1: エラーコードとメッセージキー
public final class ErrorCodes {
private ErrorCodes() {}
public static final String NOT_FOUND = "ERR_NOT_FOUND";
public static final String UNAUTHORIZED = "ERR_UNAUTHORIZED";
}
Java使い方:
log.error("code={} path={}", ErrorCodes.NOT_FOUND, path);
Java例 2: 正規表現・タイムアウト・閾値
public final class NetConst {
private NetConst() {}
public static final int CONNECT_TIMEOUT_MS = 3_000;
public static final int READ_TIMEOUT_MS = 5_000;
public static final String IPV4_REGEX =
"^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$";
}
Java例 3: ロケールやフォーマット
import java.util.Locale;
import java.time.format.DateTimeFormatter;
public final class FormatConst {
private FormatConst() {}
public static final Locale DEFAULT_LOCALE = Locale.JAPAN;
public static final DateTimeFormatter ISO_DATE = DateTimeFormatter.ISO_LOCAL_DATE;
}
Javaテストと変更に強い定数の書き方
意味のある名前で「用途」を伝える
public static final int PAGE_SIZE_DEFAULT = 50; // 何のサイズか、何の用途かを名前で表す
Java- 「値そのもの」ではなく「役割」を名前に込めると、コメントがなくても理解できます。
変更頻度に応じて分離
- よく変わるビジネス値(税率・閾値)と、ほぼ不変のシステム値(フォーマット・ディレクトリ)を分けると、改修時の影響範囲が読みやすくなります。
取りうる値が増え始めたら enum へ昇格
- 文字列定数が増え続ける場合は、型安全性を高めるため enum の導入を検討します。
仕上げのアドバイス(重要部分のまとめ)
定数は「一か所に集約」「不変」「意味のある名前」が基本です。定数クラスは final+private コンストラクタで名前空間化し、public static final で公開。可変オブジェクトは不変で持ち、用途別にファイルを分ける。取りうる値の集合には enum を使い、Constant Interface は避ける。必要なら最小限の static import——この型が身につけば、マジックナンバーや散乱文字列から解放され、変更に強いコードになります。
