定数の全体像
定数は「値を変えない」と約束した識別子です。Javaでは final を使って再代入を禁止します。クラス全体で共有する不変の値は static final にし、インスタンスごとに不変の値ならフィールドを final にします。定数化は「誤代入の防止」「意味の明確化」「保守性の向上」に直結します。
基本構文と使い分け
ローカル定数(メソッド内)
public void calc() {
final int TAX_RATE_PERCENT = 10; // メソッド内だけで使う
int priceWithTax = 100 * (100 + TAX_RATE_PERCENT) / 100;
System.out.println(priceWithTax);
}
Javafinal を付けると、その変数には再代入ができません。「途中で書き換えない意図」をコードで保証できます。
クラス定数(共有値)
public class MathConst {
public static final double PI = 3.141592653589793;
public static final int MAX_ITEMS = 1000;
}
Javastatic final は「クラスに属する一意の値」。インスタンスを作らず MathConst.PI のように参照します。外部に公開するなら public、内部だけなら private にし、必要なら getter を用意します。
命名規約とスコープ(重要ポイントの深掘り)
名前の付け方
定数は「すべて大文字+単語区切りにアンダースコア」が基本です。例:MAX_SIZE, DEFAULT_TIMEOUT_MS, USER_ROLE_ADMIN。コードを読んだ瞬間に「定数だ」と分かります。
スコープ設計
- 小さな文脈でしか使わない値は「ローカル定数」へ。
- 複数クラスから参照する値は「クラス定数(static final)」へ。
- パッケージ外へ出さないなら
privateにして、意図せぬ依存を防ぎます。
コンパイル時定数と実行時定数(重要ポイントの深掘り)
コンパイル時定数の条件
「プリミティブや String で、static final、かつ右辺がリテラルや定数式」の場合、コンパイラが値を埋め込みます(定数折りたたみ)。
public class Config {
public static final int PORT = 8080; // コンパイル時定数
public static final String HOST = "localhost"; // コンパイル時定数
public static final int TIMEOUT = 1000 * 60; // 定数式
}
Java公開APIでコンパイル時定数を変更すると、他モジュールの再コンパイルが必要になることがあります。外部に露出する値は「設定ファイル」や「実行時読み込み」を検討しましょう。
実行時に決まる定数
リテラルで決められないものは「不変だがコンパイル時ではない定数」になります。
public class Paths {
public static final java.nio.file.Path HOME = java.nio.file.Path.of(System.getProperty("user.home"));
}
Javafinal なので再代入は不可ですが、コンパイル時に埋め込まれません。
参照型の定数と「中身の不変性」
final は「参照の再代入禁止」
final StringBuilder sb = new StringBuilder();
sb.append("ok"); // 中身の変更は可能(StringBuilderは可変)
Javafinal は「参照が別のオブジェクトを指さない」だけで、オブジェクトの中身が可変なら変更できます。本当に不変を保証したいなら「不変オブジェクト」を定数にします。
public static final String TITLE = "Hello"; // Stringは不変
public static final java.util.List<String> EMPTY = java.util.List.of(); // 不変リスト
Java配列は要素が変更可能です。定数として公開するなら、変更不可のビューを返すか、コピーして渡す設計を選びましょう。
金額・区分値・設定値の定数化(実務の指針)
BigDecimal の定数(丸め・精度のため)
import java.math.BigDecimal;
public class Money {
public static final BigDecimal TAX_RATE = new BigDecimal("0.10"); // 文字列で正確に
}
Java二進小数誤差を避けたい金額・割合は BigDecimal("文字列") を使います。new BigDecimal(0.1) は誤差が入るため避けます。
区分値は enum が最強
public enum Role {
ADMIN, USER, GUEST;
}
Java「整数定数」「文字列定数」の代わりに enum を使うと、型が守られ、switch・比較・一覧が安全になります。関連情報(表示名や権限)も enum に持たせられます。
設定値はクラスにまとめる
public final class AppConfig {
private AppConfig() {}
public static final int MAX_RETRY = 3;
public static final long DEFAULT_TIMEOUT_MS = 2_000;
}
Java散らばった「魔法の数値」を1箇所へ集約すると、変更の影響を把握しやすくなります。
インターフェース定数と可視性(注意点の深掘り)
インターフェースの暗黙定数
インターフェースに書いたフィールドは自動的に public static final になります。便利ですが「どこからでも見える」ため、むやみに公開しないほうが安全です。
public interface Http {
int DEFAULT_PORT = 80; // public static final(暗黙)
}
Javaライブラリ設計では「定数用インターフェース」は避け、クラスの private 定数+必要なら getter にするほうが依存が減ります。
例題で身につける
例 1: 魔法の数値を定数に置き換える
public class Shipping {
private static final int BASE_FEE_YEN = 500;
private static final int FREE_THRESHOLD_YEN = 3_000;
public static int fee(int subtotalYen) {
return (subtotalYen >= FREE_THRESHOLD_YEN) ? 0 : BASE_FEE_YEN;
}
}
Java「何の値か」が名前で伝わり、後から条件を変えるのも簡単になります。
例 2: enum で安全な区分
public enum Plan { BASIC, PRO, ENTERPRISE }
public class Billing {
public static int monthlyFee(Plan plan) {
return switch (plan) {
case BASIC -> 1000;
case PRO -> 3000;
case ENTERPRISE -> 10000;
};
}
}
Java整数や文字列ではなく Plan 型で受けることで、無効値がそもそも渡せなくなります。
例 3: 不変オブジェクトを定数にする
public final class Texts {
private Texts() {}
public static final String WELCOME = "ようこそ";
public static final java.util.List<String> MENU = java.util.List.of("Home", "Products", "Contact");
}
JavaString と List.of は不変なので、定数として安全に共有できます。
仕上げのアドバイス(重要部分のまとめ)
定数は「名前で意味を固定し、再代入を封じる」道具です。共有値は static final、文脈限定はローカル final。コンパイル時定数と実行時定数の違いを意識し、外部公開の定数は変更の影響に注意する。不変オブジェクトを選び、配列や可変オブジェクトの公開は避ける。金額は BigDecimal("…")、区分は enum。設定値は1箇所に集約し、可視性は最小化する——この定数設計が、読みやすさと安全性を底上げします。
