StringはJavaの不変クラスです。不変クラスは、インスタンスを変更できないクラスです。Javaプログラミング言語がStringクラスのオブジェクトを不変にすることを選択するのはなぜですか?
StringはJavaの不変クラスです。不変クラスは、インスタンスを変更できないクラスです。Javaプログラミング言語がStringクラスのオブジェクトを不変にすることを選択するのはなぜですか?
回答:
この問題は、クラスのインスタンスであるとはどういう意味かという概念と強く関連しています。厳密なオブジェクト指向の用語では、クラスには関連する不変条件があります。つまり、クラスの(パブリック)メソッドからの出口で常にtrueを保持する述語です。このような概念は、たとえば、継承が明確に定義されていることを保証する上で中心的な役割を果たします(これは、リスコフ置換の原則の一部です)。
Javaの最も厄介な問題の1つは、クライアントコードがクラスの不変条件を壊すのを防ぐのが難しいことです。
たとえば、次の 'ZipCode'クラスを考えます。
class ZipCode {
private String zipCode;
public ZipCode(String value){
if(!isValidZipCode(value))
throw new IllegalArgumentException();
zipCode = value;
assert(invariant());
}
public String get() { return zipCode; }
public boolean invariant() {
return isValidZipCode( zipCode );
}
}
Stringが不変でない場合、ZipCodeのユーザーが「get」を呼び出してその後いつでも文字を変更できるため、不変条件が破られ、ZipCodeコンセプトのカプセル化によって提供される概念的な整合性が破壊されます。
この種の整合性は、大規模なシステムが有効であることを保証するために不可欠であるため、質問に対するこの回答は、実際には次のより広いものを要求します。
「JavaがC ++ constの類似物をサポートしないのはなぜですか、少なくとも、ライブラリクラスの多くの不変バージョンを提供しないのですか?」
文字列や日付などは当然のことながら値です。C ++の用語では、コピーコンストラクター、代入演算子、および等価演算子があることを期待しますが、それらのアドレスを取得することは決してありません。したがって、それらが個別にヒープに割り当てられるとは想定していません。仮想メソッドは意味がありません。
ドメインオブジェクトは当然参照です。C ++のものには、コピーコンストラクター、代入演算子、または等価演算子がありません(同一の場合にのみ等価です)。私たちはそれらのアドレスを取得することができ、ヒープが割り当てられることを期待しています。メソッドは一般に仮想です。
Javaには値クラスはありません。参照クラスのみです。値は不変オブジェクトで偽造されています。これは文字列には当てはまりますが、残念ながら日付には当てはまりません。Java日付の可変性により、頻繁に問題が発生し、現在は推奨されていません。たとえば、可変値はハッシュの基礎として使用できません。
Javaは、セキュリティが制限された環境でプログラムのコードのサブセクションを実行できるように設計されています。この要件が実装された方法は、特定の重要な操作(ファイルを開くなど)のパラメーターへのアクセス権が与えられ、操作を続行できるかどうかを尋ねるスレッドに「SecurityManager」を設定することでした。Java文字列が変更可能である場合、プログラムは2つのスレッドを作成することでこのような制限を回避できます。1つは許可されるファイルを開く操作を実行し、もう1つはファイル名を格納する文字列を許可されないものに変更しました。次に、セキュリティマネージャが元の文字列を読み取り、操作を受け入れる可能性があります。この操作は、2番目の(許可されていない)ファイルを開く前にファイルを開くコードに渡されます。
後者の可能性は、そのようなすべての操作の実行を遅くし、実装にバグが含まれる可能性が高くなるため、不変の文字列を使用することが最も賢明な決定でした。
より一般的には、不変オブジェクトは、防御的なコピーを作成せずに共有できるため(セキュリティが重要でないコードでも、ソースデータが変更されたときにバグを防ぐために必要な場合がある)、この要件がなくても決定が可能なため、有用です。合理的なもの。