次のような例を見てきました。
public class MaxSeconds {
public static final int MAX_SECONDS = 25;
}
そして、定数をラップするための Constants クラスを用意して、それらを static final と宣言できると仮定しました。私は事実上 Java をまったく知らないので、これが定数を作成する最良の方法であるかどうか疑問に思っています。
それは完全に受け入れられ、おそらく標準でさえあります。
(public/private) static final TYPE NAME = VALUE;
ここTYPE
で、 はタイプ、NAME
はスペースにアンダースコアを付けたすべて大文字の名前、VALUE
は定数値です。
独自のクラスまたはインターフェイスに定数を配置しないことを強くお勧めします。
補足として: final と宣言され、可変である変数は引き続き変更できます。ただし、変数が別のオブジェクトを指すことはできません。
例えば:
public static final Point ORIGIN = new Point(0,0);
public static void main(String[] args){
ORIGIN.x = 3;
}
これは正当でありORIGIN
、(3, 0) のポイントになります。
単一の定数クラスを持つことは強くお勧めしません。当時は良い考えに思えるかもしれませんが、開発者が定数を文書化することを拒否し、クラスが 500 を超える定数を含むように成長し、それらの定数が互いにまったく関連していない (アプリケーションのまったく異なる側面に関連している) 場合、これは通常、定数ファイルは完全に読み取り不能になります。その代わり:
定数を保持するためだけにインターフェイスを使用するのは悪い習慣です ( Josh Bloch によって定数インターフェイス パターンと名付けられました)。ジョシュは次のようにアドバイスしています。
定数が既存のクラスまたはインターフェイスに強く結び付けられている場合は、それらをクラスまたはインターフェイスに追加する必要があります。たとえば、Integer や Double などのボックス化されたすべての数値プリミティブ クラスは、MIN_VALUE および MAX_VALUE 定数をエクスポートします。定数が列挙型のメンバーとして最適に表示される場合は、それらを列挙 型でエクスポートする必要があります。それ以外の場合は、インスタンス化できないユーティリティ クラスを使用して定数をエクスポートする必要があります。
例:
// Constant utility class
package com.effectivejava.science;
public class PhysicalConstants {
private PhysicalConstants() { } // Prevents instantiation
public static final double AVOGADROS_NUMBER = 6.02214199e23;
public static final double BOLTZMANN_CONSTANT = 1.3806503e-23;
public static final double ELECTRON_MASS = 9.10938188e-31;
}
命名規則について:
慣例により、そのようなフィールドには大文字で構成され、単語がアンダースコアで区切られた名前が付けられます。これらのフィールドには、プリミティブ値または不変オブジェクトへの参照が含まれていることが重要です。
効果的なJava(第2版)では、定数に静的intの代わりに列挙型を使用することをお勧めします。
ここにJavaの列挙型に関する優れた記事があります:http: //java.sun.com/j2se/1.5.0/docs/guide/language/enums.html
その記事の終わりに提起された質問は次のとおりであることに注意してください。
では、いつ列挙型を使用する必要がありますか?
の答えで:
定数の固定セットが必要なときはいつでも
インターフェイスの使用は避けてください。
public interface MyConstants {
String CONSTANT_ONE = "foo";
}
public class NeddsConstant implements MyConstants {
}
魅力的ですが、カプセル化に違反し、クラス定義の区別が曖昧になります。
私は次のアプローチを使用します。
public final class Constants {
public final class File {
public static final int MIN_ROWS = 1;
public static final int MAX_ROWS = 1000;
private File() {}
}
public final class DB {
public static final String name = "oups";
public final class Connection {
public static final String URL = "jdbc:tra-ta-ta";
public static final String USER = "testUser";
public static final String PASSWORD = "testPassword";
private Connection() {}
}
private DB() {}
}
private Constants() {}
}
たとえば、私はConstants.DB.Connection.URL
定数を取得するために使用します。私にとっては、より「オブジェクト指向」に見えます。
別のクラスで static final 定数を作成すると、問題が発生する可能性があります。Java コンパイラは実際にこれを最適化し、定数の実際の値をそれを参照する任意のクラスに配置します。
後で「定数」クラスを変更し、そのクラスを参照する他のクラスでハード再コンパイルを行わないと、古い値と新しい値の組み合わせが使用されることになります。
これらを定数と考えるのではなく、構成パラメーターと考えて、それらを管理するクラスを作成します。値を非最終的なものにし、ゲッターの使用を検討してください。将来、これらのパラメーターの一部を実際にユーザーまたは管理者が構成できるようにする必要があると判断した場合は、はるかに簡単に実行できるようになります。
最も犯しやすい間違いは、Constants のような一般的な名前で呼び出されるグローバルにアクセス可能なクラスを作成することです。これは単にゴミが散らばり、システムのどの部分がこれらの定数を使用しているかを把握する能力をすべて失います。
代わりに、定数はそれらを「所有」するクラスに入る必要があります。TIMEOUT という定数はありますか? おそらく Communications() または Connection() クラスに入るはずです。MAX_BAD_LOGINS_PER_HOUR? User() に入ります。などなど。
他に考えられる用途は、実行時に「定数」を定義できるが、ユーザーが簡単に変更できない場合の Java .properties ファイルです。これらを .jar にパッケージ化して、Class resourceLoader で参照できます。
それが正しい道です。
通常、定数は検出できないため、個別の「定数」クラスには保持されません。定数が現在のクラスに関連している場合、そこに保持しておくと次の開発者に役立ちます。
私は、インターフェースを使用することが道ではないことに同意します。このパターンを回避することは、BlochのEffective Javaに独自のアイテム(#18)を持っています。
Blochが定数インターフェイスパターンに対して行う議論は、定数の使用は実装の詳細であるというものですが、定数を使用するためのインターフェイスを実装すると、エクスポートされたAPIでその実装の詳細が公開されます。
public|private static final TYPE NAME = VALUE;
パターンは、定数を宣言するための良い方法です。個人的には、すべての定数を格納するために別のクラスを作成することは避けたほうがよいと思いますが、個人的な好みとスタイル以外に、これを行わない理由は見たことがありません。
定数を列挙型として適切にモデル化できる場合は、1.5以降で使用可能な列挙型構造を検討してください。
1.5より前のバージョンを使用している場合でも、通常のJavaクラスを使用してタイプセーフな列挙を実行できます。(詳細については、このサイトを参照してください)。
列挙はどうですか?
定数よりもゲッターを使用することを好みます。これらのゲッターは定数値を返すpublic int getMaxConnections() {return 10;}
ことがありますが、定数を必要とするものはすべてゲッターを通過します。
利点の 1 つは、プログラムが定数を超えた場合 (構成可能にする必要があることがわかった場合)、getter が定数を返す方法を変更するだけでよいことです。
もう 1 つの利点は、定数を変更するために、それを使用するすべてのものを再コンパイルする必要がないことです。static final フィールドを参照すると、その定数の値はそれを参照する任意のバイトコードにコンパイルされます。
上記のコメントに基づいて、これは昔ながらのグローバル定数クラス (public static final 変数を持つ) を、次のような方法で列挙型の同等のものに変更するための良いアプローチだと思います。
public class Constants {
private Constants() {
throw new AssertionError();
}
public interface ConstantType {}
public enum StringConstant implements ConstantType {
DB_HOST("localhost");
// other String constants come here
private String value;
private StringConstant(String value) {
this.value = value;
}
public String value() {
return value;
}
}
public enum IntConstant implements ConstantType {
DB_PORT(3128),
MAX_PAGE_SIZE(100);
// other int constants come here
private int value;
private IntConstant(int value) {
this.value = value;
}
public int value() {
return value;
}
}
public enum SimpleConstant implements ConstantType {
STATE_INIT,
STATE_START,
STATE_END;
}
}
したがって、次のように参照できます。
Constants.StringConstant.DB_HOST
優れたオブジェクト指向設計では、公開されている多くの定数は必要ありません。ほとんどの定数は、ジョブを実行するために必要なクラスにカプセル化する必要があります。
これに答えるには、ある程度の意見があります。まず、Java の定数は一般に public、static、final として宣言されています。理由は次のとおりです。
public, so that they are accessible from everywhere
static, so that they can be accessed without any instance. Since they are constants it
makes little sense to duplicate them for every object.
final, since they should not be allowed to change
インターフェイスは一般的に実装されることが期待されているため、CONSTANTS アクセサー/オブジェクトにインターフェイスを使用することは決してありません。これはおかしいと思いませんか:
String myConstant = IMyInterface.CONSTANTX;
代わりに、いくつかの小さなトレードオフに基づいて、いくつかの異なる方法から選択するので、必要なものによって異なります。
1. Use a regular enum with a default/private constructor. Most people would define
constants this way, IMHO.
- drawback: cannot effectively Javadoc each constant member
- advantage: var members are implicitly public, static, and final
- advantage: type-safe
- provides "a limited constructor" in a special way that only takes args which match
predefined 'public static final' keys, thus limiting what you can pass to the
constructor
2. Use a altered enum WITHOUT a constructor, having all variables defined with
prefixed 'public static final' .
- looks funny just having a floating semi-colon in the code
- advantage: you can JavaDoc each variable with an explanation
- drawback: you still have to put explicit 'public static final' before each variable
- drawback: not type-safe
- no 'limited constructor'
3. Use a Class with a private constructor:
- advantage: you can JavaDoc each variable with an explanation
- drawback: you have to put explicit 'public static final' before each variable
- you have the option of having a constructor to create an instance
of the class if you want to provide additional functions related
to your constants
(or just keep the constructor private)
- drawback: not type-safe
4. Using interface:
- advantage: you can JavaDoc each variable with an explanation
- advantage: var members are implicitly 'public static final'
- you are able to define default interface methods if you want to provide additional
functions related to your constants (only if you implement the interface)
- drawback: not type-safe
FWIW、秒単位のタイムアウト値は、おそらく構成設定(プロパティファイルから読み取られるか、Spring のように注入によって読み取られる)であり、定数ではないはずです。
違いはなんですか
1.
public interface MyGlobalConstants {
public static final int TIMEOUT_IN_SECS = 25;
}
2.
public class MyGlobalConstants {
private MyGlobalConstants () {} // Prevents instantiation
public static final int TIMEOUT_IN_SECS = 25;
}
MyGlobalConstants.TIMEOUT_IN_SECS
この定数が必要な場所ならどこでも使用
できます。どちらも同じだと思います。
final
任意の型の定数は、クラス (修飾子を持つメンバー変数) 内の不変プロパティを作成することによって宣言できます。通常、修飾子static
と public
修飾子も提供されます。
public class OfficePrinter {
public static final String STATE = "Ready";
}
定数の値が選択肢の n タプル (列挙型など) からの選択を示すアプリケーションは数多くあります。この例では、割り当てられる可能性のある値を制限する列挙型を定義することを選択できます (つまり、型安全性が向上します)。
public class OfficePrinter {
public enum PrinterState { Ready, PCLoadLetter, OutOfToner, Offline };
public static final PrinterState STATE = PrinterState.Ready;
}
単一の一般的な定数クラスはお勧めできません。定数は、それらが最も論理的に関連しているクラスと一緒にグループ化する必要があります。
あらゆる種類の変数 (特に列挙型) を使用するのではなく、メソッドを使用することをお勧めします。変数と同じ名前のメソッドを作成し、変数に割り当てた値を返すようにします。ここで、変数を削除し、その変数へのすべての参照を、作成したばかりのメソッドへの呼び出しに置き換えます。定数が十分に汎用的であり、それを使用するためだけにクラスのインスタンスを作成する必要がない場合は、定数メソッドをクラス メソッドにします。
クラスを定数と同じ(大文字と小文字を除いて)とは呼びません...すべての定数が存在する「設定」、「値」、または「定数」の少なくとも1つのクラスがあります。それらが多数ある場合は、それらを論理定数クラス (UserSettings、AppSettings など) にグループ化します。
定数の場合、 Enum の方が良い選択です。ここに例があります
パブリック クラス myClass {
public enum myEnum {
Option1("String1", 2),
Option2("String2", 2)
;
String str;
int i;
myEnum(String str1, int i1) { this.str = str1 ; this.i1 = i }
}
私がそれを行う方法の 1 つは、定数値を持つ「グローバル」クラスを作成し、定数へのアクセスが必要なクラスで静的インポートを行うことです。
私はstatic final
定数を宣言するために使用し、ALL_CAPS 命名表記を使用します。私は、すべての定数が 1 つのインターフェイスにまとめられている実際のインスタンスをかなり多く見てきました。いくつかの投稿では、これを悪い習慣と呼んでいますが、それは主に、それがインターフェイスの目的ではないためです。インターフェイスはコントラクトを強制する必要があり、無関係な定数を配置する場所であってはなりません。定数のセマンティクスが特定のクラスに属していない場合は、(プライベート コンストラクターを介して) インスタンス化できないクラスにそれをまとめても問題ありません( es)。私は常に、最も関連性の高いクラスに定数を配置します。これは理にかなっていて、保守も簡単だからです。
列挙型は、値の範囲を表すのに適していますが、絶対値 (例: TIMEOUT = 100 ミリ秒) に重点を置いてスタンドアロンの定数を格納している場合は、この方法を使用できますstatic final
。
さらに一歩進めると、グローバルに使用される定数をインターフェイスに配置して、システム全体で使用できるようにすることができます。例えば
public interface MyGlobalConstants {
public static final int TIMEOUT_IN_SECS = 25;
}
ただし、それを実装しないでください。完全修飾クラス名を介してコードで直接参照するだけです。
static final
が私の好みenum
です。アイテムが実際に列挙可能な場合にのみ使用します。