定数の2つの側面を区別する必要があります。
- 開発時に知られている値の名前。保守性を高めるために導入します。
- コンパイラーが使用できる値。
そして、関連する第3の種類があります。値が変化しない変数、つまり値の名前です。これらの不変変数と定数の違いは、値が決定/割り当て/初期化されるときです。変数は実行時に初期化されますが、定数の値は開発中に既知です。開発中に値がわかっている場合がありますが、実際には初期化中にのみ作成されるため、この区別は少し曖昧です。
ただし、コンパイル時に定数の値がわかっている場合、コンパイラはその値を使用して計算を実行できます。たとえば、Java言語には定数式の概念があります。定数式は、プリミティブまたは文字列のリテラル、定数式に対する操作(キャスト、加算、文字列の連結など)、および定数変数のみで構成される式です。[ JLS§15.28 ]定数変数はfinal
、定数式で初期化される変数です。[JLS§4.12.4]したがって、Javaの場合、これはコンパイル時の定数です。
public static final int X = 7;
これは、定数変数が複数のコンパイル単位で使用され、宣言が変更されたときに興味深いものになります。考慮してください:
これらのファイルをコンパイルすると、B.class
バイトコードは定数変数であるY = 9
ためフィールドを宣言しB.Y
ます。
ただし、A.X
変数を別の値(たとえば、X = 0
)に変更し、A.java
ファイルのみを再コンパイルするB.Y
と、古い値が引き続き参照されます。この状態A.X = 0, B.Y = 9
は、ソースコードの宣言と矛盾しています。楽しいデバッグ!
これは、定数を変更してはならないという意味ではありません。定数は、ソースコードで説明なしで表示されるマジックナンバーよりも明確に優れています。ただし、パブリック定数の値はパブリックAPIの一部です。これはJavaに固有のものではありませんが、C ++および別のコンパイル単位を備えた他の言語でも発生します。これらの値を変更する場合、すべての依存コードを再コンパイルする必要があります。つまり、クリーンコンパイルを実行します。
定数の性質によっては、開発者による誤った仮定につながる可能性があります。これらの値が変更されると、バグが発生する可能性があります。たとえば、定数のセットは、特定のビットパターンを形成するように選択される場合がありますpublic static final int R = 4, W = 2, X = 1
。これらが異なる構造を形成するように変更さR = 0, W = 1, X = 2
れるboolean canRead = perms & R
と、既存のコードなどが正しくなくなります。そして、その後の楽しみがInteger.MAX_VALUE
変わることを考えてみてください!ここには修正はありません。いくつかの定数の値は本当に重要であり、単純に変更できないことを覚えておくことが重要です。
しかし、上記の制限が考慮されている限り、定数を変更しても大部分は問題ありません。特定の値ではなく、意味が重要な場合、定数は安全に変更できます。これは、たとえば、BORDER_WIDTH = 2
またはTIMEOUT = 60; // seconds
などのテンプレートの場合です。API_ENDPOINT = "https://api.example.com/v2/"
ただし、おそらくそれらの一部またはすべては、コードではなく構成ファイルで指定する必要があります。