定数の値は時間とともに変化しますか?


28

開発段階では、同じ実行で修正する必要がある特定の変数がありますが、時間をかけて修正する必要がある場合があります。たとえば、booleanデバッグモードを通知するには、通常は実行しないプログラムで処理を行います。

これらの値を定数、つまりfinal static int CONSTANT = 0Java に含めるのは悪いスタイルですか?実行中は定数が同じままであることを知っていますが、もちろん、計画外の変更を除いて、開発全体で同じであると想定されていますか?

同様の質問を検索しましたが、私の質問に完全に一致するものは見つかりませんでした。


11
好奇心が強いのですが、なぜこれらを変更するのが悪いスタイルだと思われますか?
ビンセントサ

36
既知の数学的値を持つ定数を使用して物理的特性をモデル化していない限り、すべてがいつか変わる可能性があります。
ベリンロリチュ

19
ソフトウェアはソフトです。
エリックエイド

10
@GregT私は同意しないだろう。finalプログラムが値を変更しないことをコンパイラーが強制的に保証します。プログラマがソースコードで割り当てられた値を変更したいという理由だけで、私はそれを省きません。
アレクサンダー-モニカーの復活

10
完全な答えを明確にする時間はあまりありませんが、同僚の懸念は定数ではなく、定数として明示される傾向のある設定値のコードインライン化に懸念があると思います。...定数は、誤ってgravityゲーム中/実行中に割り当てるなどの愚かなミスから保護します。必ずしもgravityすべての惑星で同じというわけではありません...それは、健全な解決策はgravity定数を作ることplanetですが、関連するスコープの開始時にファイルまたはデータベースからそれを引き出すことです。
svidgen

回答:


6

Javaでは、コンパイラによって静的な最終定数を値として、それらを使用するコードにコピーできます。この結果、コードの新しいバージョンをリリースし、定数を使用しているダウンストリームの依存関係がある場合、そのコードの定数は、ダウンストリームコードが再コンパイルされない限り更新されません。これは、ソースコードが正しくてもバイナリコードが正しくないため、新しい値を期待するコードでその定数を使用する場合に問題になる可能性があります。

これは、ソース互換性とバイナリ互換性が同じではない非常に少数のケース(唯一のケース)の1つであるため、Javaの設計におけるいぼです。この場合を除き、依存関係のユーザーが再コンパイルしなくても、依存関係を新しいAPI互換バージョンと交換できます。Javaの依存関係が一般的に管理される方法を考えると、これは明らかに非常に重要です。

さらに悪いことに、コードは有用なエラーを生成するのではなく、静かに間違ったことをするだけです。依存関係を互換性のないクラス定義またはメソッド定義のバージョンに置き換えると、クラスローダーまたは呼び出しエラーが発生します。これにより、少なくとも問題の内容に関する手がかりが得られます。値のタイプを変更していない限り、この問題は単に不可解なランタイムの誤動作として表示されます。

さらに厄介なのは、今日のJVMが実行時にすべての定数をパフォーマンスのペナルティなしで簡単にインライン化できることです(おそらく、とにかく読み込まれている定数を定義するクラスを読み込む必要はありません)。 。また、以前のコンパイラでコンパイルされたコードは正しくないため、言語を変更できません。バグ互換性が再び攻撃されます。

このため、静的な最終値をまったく変更しないことを勧める人もいます。広く配布され、未知の時点で未知の方法で更新される可能性のあるライブラリの場合、これは良い習慣です。

独自のコード、特に依存関係階層の最上部では、おそらくこれで十分です。ただし、これらの場合は、定数をパブリック(または保護)にする必要があるかどうかを検討してください。定数がパッケージの可視性のみの場合、状況とコード標準に応じて、パッケージ全体が常に一度に再コンパイルされ、問題は解決します。定数がプライベートの場合、問題はなく、いつでも変更できます。


85

const宣言されたグローバル定数を含むソースコードのすべては、ソフトウェアの新しいリリースで変更される場合があります。

キーワードconst(またはfinalJava)は、プログラムのこのインスタンスの実行中にこの変数が変更されないことコンパイラに通知するためにあります。これ以上何もない。次のメンテナーにメッセージを送信したい場合は、ソースでコメントを使用してください。それが彼らの目的です。

// DO NOT CHANGE without consulting with the legal department!
// Get written consent form from them before release!
public const int LegalLimitInSeconds = ...

あなたの将来の自己とコミュニケーションをとるためのより良い方法です。


11
未来の自分へのこのコメントは本当に気に入りました。
GregT

4
TaxRateビーイングは、public私が神経質になります。この変更の影響を受けるのは営業部門のみであり、税金を請求するサプライヤーも対象ではありません。そのコメントが書かれてからコードベースで何が起こったのか誰が知っていますか。
candied_orange

3
@IllusiveBrianは定数の使用を批判していませんでした。最新のコメントを信頼することに対して警告していました。変更する前に、何かの使用方法を常に確認してください。
candied_orange

8
これはJavaにとって良いアドバイスです。他の言語では異なる場合があります。たとえば、C#でconst値が呼び出しサイトにバインドされる方法のため、Math.piのように、public constフィールドは決して変化しないものにのみ使用する必要があります。ライブラリを作成する場合、開発中または新しいバージョンで変更される可能性のあるものpublic static readonlyは、ライブラリのユーザーに問題を引き起こさないようにする必要があります。
-GrandOpener

6
別の例を選択する必要があります...お金の値は決して浮動小数点であってはなりません!
corsiKa

13

定数の2つの側面を区別する必要があります。

  • 開発時に知られている値の名前。保守性を高めるために導入します。
  • コンパイラーが使用できる値。

そして、関連する第3の種類があります。値が変化しない変数、つまり値の名前です。これらの不変変数と定数の違いは、値が決定/割り当て/初期化されるときです。変数は実行時に初期化されますが、定数の値は開発中に既知です。開発中に値がわかっている場合がありますが、実際には初期化中にのみ作成されるため、この区別は少し曖昧です。

ただし、コンパイル時に定数の値がわかっている場合、コンパイラはその値を使用して計算を実行できます。たとえば、Java言語には定数式の概念があります。定数式は、プリミティブまたは文字列のリテラル、定数式に対する操作(キャスト、加算、文字列の連結など)、および定数変数のみで構成される式です。[ JLS§15.28 ]定数変数はfinal、定数式で初期化される変数です。[JLS§4.12.4]したがって、Javaの場合、これはコンパイル時の定数です。

public static final int X = 7;

これは、定数変数が複数のコンパイル単位で使用され、宣言が変更されたときに興味深いものになります。考慮してください:

  • A.java

    public class A { public static final int X = 7; }
  • B.java

    public class B { public static final int Y = A.X + 2; }

これらのファイルをコンパイルすると、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/"ただし、おそらくそれらの一部またはすべては、コードではなく構成ファイルで指定する必要があります。


5
私はこの分析が好きです。定数の使い方を理解している限り、定数を自由に変更できます。
candied_orange

+1 C#は、パブリック定数の同じ問題から「苦しみます」。
レジナルドブルー

6

定数は、アプリケーションランタイムの有効期間中のみ一定であることが保証されています。それが真実である限り、言語機能を利用しない理由はありません。同じ目的で定数フラグとコンパイラフラグを使用した場合の結果を知る必要があるだけです。

  • 定数はアプリケーションのスペースを占有します
  • コンパイラフラグ
  • 定数によってオフにされたコードは、最新のリファクタリングツールを使用して更新および変更できます
  • コンパイラフラグによってオフにされたコードは

図形の境界ボックスの描画をオンにするアプリケーションを維持して、図形の描画方法をデバッグできるようにしたので、問題が発生しました。リファクタリング後、コンパイラフラグによってオフにされたすべてのコードはコンパイルされませんでした。その後、コンパイラフラグを意図的にアプリケーション定数に変更しました。

トレードオフがあることを示すために言っています。いくつかのブール値の重みにより、アプリケーションがメモリ不足になったり、スペースを取りすぎたりすることはありませんでした。定数が、コード内のすべてを本質的に処理する大きなオブジェクトである場合、これは当てはまらない可能性があります。不要になったオブジェクトへのすべての参照を削除しない場合、オブジェクトがメモリリークの原因になっている可能性があります。

ユースケースと、定数を変更する理由を評価する必要があります。

私は単純な毛布の声明のファンではありませんが、一般的にあなたの先輩は正しいです。何かが頻繁に変更される場合は、構成可能なアイテムにする必要があります。たとえば、IsInDebugMode = true一部のコードが破損しないように保護する場合は、同僚に定数を納得させることができます。ただし、アプリケーションをリリースするよりも頻繁に変更する必要があるものもあります。その場合は、適切なタイミングでその値を変更する方法が必要です。の例を使用できますTaxRate = .065。これはコードをコンパイルするときに当てはまるかもしれませんが、新しい法律により、アプリケーションの次のバージョンをリリースする前に変更される可能性があります。これは、何らかのストレージメカニズム(ファイルやデータベースなど)から更新する必要があるものです


「コンパイラフラグ」とはどういう意味ですか?おそらく、マクロ/定義と#ifdefs をサポートするCプリプロセッサーと同様のコンパイラー機能ですか?これらはソースコードのテキスト置換に基づいているため、プログラミング言語のセマンティクスの一部ではありません。Javaにはプリプロセッサがないことに注意してください。
アモン

@ amon、Javaはそうではないかもしれませんが、いくつかの言語はそうです。私は#ifdefフラグを意味します。これらはCのセマンティクスの一部ではありませんが、C#の一部です。私は言語不可知論のより大きな文脈のために書いていました。
ベリンロリチュ

「メモリの無駄」の議論は無意味だと思います。インライン化とツリーの揺れは、リリースモードオプティマイザーのほぼ普遍的なステップです。
アレクサンダー-モニカーの復活

@アレクサンダー、同意します。ただし、注意する必要があります。
ベリンロリチュ

1
「定数はアプリケーション領域を占有します」-メモリが1〜2バイトしかないマイクロコントローラー用の組み込みアプリケーションを開発しているのでなければ、そのようなことを考えてはいけません。
-vsz

2

const#defineまたはfinalコンパイラ・ヒント(そのノートで#define、実際にヒント、そのAマクロとはるかに強力ではありません)。これは、プログラムの実行中に値が変化せず、さまざまな最適化が行われる可能性があることを示しています。

ただし、コンパイラーのヒントとして、コンパイラーはプログラマーが常に期待しているとは限らないことを行います。特に、javacはaをインライン化するstatic final int FOO = 42;ため、FOO使用される場所であればどこでも実際のコンパイル済みバイトコードが読み取られます42

誰かが他のコンパイル単位(.javaファイル)を再コンパイルせずに値を変更するまで、これはそれほど大きな驚きではありません-そして42、バイトコードに残ります(javacの静的な最終変数のインライン化を無効にすることは可能ですか?)。

何かstatic finalを作るということは、それが永遠に続くということであり、それを変えることは本当に大したことですprivate

のようなものの定数final static int ZERO = 0は問題ではありません。 final static double TAX_RATE = 0.55(お金とダブルであることは別として、BigDecimalを使用する必要がありますが、プリミティブではないためインライン化されません)は問題であり、使用場所については慎重に検討する必要があります。


ゼロの小さな値の場合。

3
is a problem and should be examined with great care for where it is used.なぜそれが問題なのですか?
アレクサンダー-モニカの復活

1

名前が示唆するように、定数は実行中に変更されるべきではなく、私の意見では、定数は長期間変更されないように定義されています(詳細については、このSOの質問をご覧ください)。

フラグが必要になった場合(開発モードなど)、代わりに構成ファイルまたは起動パラメーターを使用する必要があります(多くのIDEはプロジェクトごとの起動パラメーターの構成をサポートしています。関連ドキュメントを参照してください)。このようにして、そのようなモードを使用する柔軟性を維持し、コードが生産的になるたびに変更することを忘れることはできません。


0

実行ごとに変更できることは、ソースコードで定数を定義する最も重要なポイントの1つです。

定数は、ソースコードの有効期間中に必要に応じて値を変更するために、明確に文書化された(ある意味で)場所を提供します。また、この場所で定数を変更すると、それが意味するもののすべての出現が実際に変更されるという約束です。

悪い例として、実際にキーワードを持っている言語で評価する定数を持つことは意味がありません。残酷な冗談を除いて、決して一度も宣言しないでしょう。TRUEtruetrueTRUE=false

もちろん、例の短縮コードの定数の他の用途、(そこにあるCO_NAME = 'My Great World Unique ACME Company'重複を避ける)、(PI=3.141規則を設定するには)、( TRUE=1)、または何が、これらに限定された位置持つ変更定数は確かに最も顕著なものの一つです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.