Javaで class immutable を作成するには、次のようにする必要があります。
手順3が必要な理由クラスfinal
をマークする必要があるのはなぜですか?
クラスfinal
をマークしないと、一見不変のクラスを突然変更可能にする可能性があります。たとえば、次のコードを検討してください。
public class Immutable {
private final int value;
public Immutable(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
ここで、次のことをするとします。
public class Mutable extends Immutable {
private int realValue;
public Mutable(int value) {
super(value);
realValue = value;
}
public int getValue() {
return realValue;
}
public void setValue(int newValue) {
realValue = newValue;
}
public static void main(String[] arg){
Mutable obj = new Mutable(4);
Immutable immObj = (Immutable)obj;
System.out.println(immObj.getValue());
obj.setValue(8);
System.out.println(immObj.getValue());
}
}
Mutable
サブクラスでは、getValue
の動作をオーバーライドして、サブクラスで宣言された新しい可変フィールドを読み取ります。その結果、最初は不変に見えるクラスは実際には不変ではありません。このMutable
オブジェクトをImmutable
オブジェクトが予想される場所に渡すことができます。これにより、オブジェクトが本当に不変であると仮定すると、コードに非常に悪いことが起こります。基本クラスfinal
をマークすると、これが発生しなくなります。
お役に立てれば!
多くの人が信じていることに反して、不変クラスfinal
を作成することはnotが必要です。
不変クラスを作成するための標準引数final
は、これを行わないと、サブクラスが可変性を追加できるため、スーパークラスの規約に違反する可能性があるということです。クラスのクライアントは不変性を想定しますが、その下から何かが変化すると驚くでしょう。
この引数を論理的な極端にすると、allメソッドをfinal
にする必要があります。そうしないと、サブクラスがスーパークラスのコントラクトに準拠しない方法。興味深いのは、ほとんどのJavaプログラマーがこれをばかげていると見ていることですが、不変クラスはfinal
である必要があるという考えには何とかして大丈夫です。一般に、Javaプログラマーと関係があるのではないかと思われます。不変性の概念に完全に満足しておらず、おそらくfinal
キーワードの複数の意味に関連するある種のあいまいな考えJava。
スーパークラスのコントラクトに準拠することは、コンパイラーによって強制できる、または常に実行する必要があるものではありません。コンパイラーは、コントラクトの特定の側面を強制できます(例:メソッドの最小セットとその型シグネチャ)しかし、コンパイラーによって実施できない典型的な契約の多くの部分があります。
不変性はクラスのコントラクトの一部です。これは、クラス(およびすべてのサブクラス)ができないことを実行することを示しているため、人々が慣れているものとは少し異なります。ほとんどのJava(および一般的にOOP)プログラマーは、契約を、クラスcanが何をするかではなく、何をするかに関するものと考える傾向があると思いますitできませんできません。
不変性は、単一のメソッドだけでなく、インスタンス全体にも影響を及ぼしますが、これはJavaのequals
およびhashCode
の動作と実際にはそれほど変わりません。これらの2つのメソッドには、Object
で特定のコントラクトがレイアウトされています。このコントラクトは、これらのメソッドcannotが行うことを非常に注意深くレイアウトします。このコントラクトは、サブクラスでより具体的になります。 equals
またはhashCode
を契約に違反する方法でオーバーライドするのは非常に簡単です。実際、これら2つのメソッドのうち1つだけを他のメソッドなしでオーバーライドした場合、契約に違反している可能性があります。これを避けるために、equals
とhashCode
をfinal
でObject
と宣言する必要がありますか?ほとんどの人はそうすべきではないと主張するだろう。同様に、不変クラスfinal
を作成する必要はありません。
ただし、不変であろうとなかろうと、ほとんどのクラスは、おそらくshouldfinal
でなければなりません。 Effective Java Second EditionItem 17:「継承のための設計と文書化、または禁止する」を参照してください。
したがって、ステップ3の正しいバージョンは次のようになります。「クラスをファイナルにするか、サブクラス化の設計時に、すべてのサブクラスが不変であり続ける必要があることを明確に文書化します。」
クラス全体を最終的にマークしないでください。
他の回答のいくつかで述べられているように、不変クラスを拡張できる正当な理由があるため、クラスをfinalとしてマークすることは常に良い考えではありません。
プロパティを非公開および最終としてマークし、「契約」を保護する場合は、ゲッターを最終としてマークすることをお勧めします。
このようにして、クラスの拡張を許可することができます(はい、おそらく可変クラスによっても可能です)が、クラスの不変の側面は保護されます。プロパティはプライベートであり、アクセスできません。これらのプロパティのゲッターは最終的なものであり、オーバーライドできません。
不変クラスのインスタンスを使用する他のコードは、それが渡されるサブクラスが他のアスペクトで可変であっても、クラスの不変アスペクトに依存できます。もちろん、クラスのインスタンスを取得するため、これらの他の側面についても知りません。
最終的なものではない場合、セッターの提供、プライベート変数のシャドウイング、基本的な変更可能化など、誰でもクラスを拡張して好きなことを実行できます。
それは、クラスを拡張する他のクラスを制限します。
finalクラスは他のクラスによって拡張できません。
クラスが不変にしたいクラスを拡張する場合、継承の原則により、クラスの状態が変わる可能性があります。
「変更される可能性がある」ことを明確にしてください。サブクラスは、メソッドのオーバーライド(templatetypedef/Ted Hop answerなど)を使用するなど、スーパークラスの動作をオーバーライドできます。
不変クラスを作成するために、クラスをfinalとしてマークすることは必須ではありません。
Javaクラス自体「BigInteger」クラスは不変ですが、最終的なものではありません。
実際、不変性とは、オブジェクトが作成されたオブジェクトを変更できないという概念です。
JVMの観点から考えてみましょう。JVMの観点からは、すべてのスレッドはオブジェクトの同じコピーを共有する必要があり、スレッドはオブジェクトにアクセスする前に完全に構築され、構築後にオブジェクトの状態は変化しません。
不変性とは、オブジェクトの作成後にオブジェクトの状態を変更する方法がないことを意味します。これは、クラスが不変であることをコンパイラに認識させる3つの経験則によって実現されます。
詳細については、以下のURLを参照してください
http://javaunturnedtopics.blogspot.in/2016/07/can-we-create-immutable-class-without.html
あなたがそれを最終的なものにしないなら、私はそれを拡張し、それを変更不可能にすることができます。
public class Immutable {
privat final int val;
public Immutable(int val) {
this.val = val;
}
public int getVal() {
return val;
}
}
public class FakeImmutable extends Immutable {
privat int val2;
public FakeImmutable(int val) {
super(val);
}
public int getVal() {
return val2;
}
public void setVal(int val2) {
this.val2 = val2;
}
}
これで、FakeImmutableをImmutableを期待するクラスに渡すことができ、期待されるコントラクトとして動作しません。
次のクラスがfinal
ではなかったとします:
public class Foo {
private int mThing;
public Foo(int thing) {
mThing = thing;
}
public int doSomething() { /* doesn't change mThing */ }
}
サブクラスでさえmThing
を変更できないため、明らかに不変です。ただし、サブクラスは変更可能です。
public class Bar extends Foo {
private int mValue;
public Bar(int thing, int value) {
super(thing);
mValue = value;
}
public int getValue() { return mValue; }
public void setValue(int value) { mValue = value; }
}
タイプFoo
の変数に割り当て可能なオブジェクトは、mmutableであることが保証されなくなりました。これは、ハッシュ、平等、並行性などの問題を引き起こす可能性があります。
Equals()のデフォルトの意味は、参照等価と同じです。不変のデータ型の場合、これはほとんど常に間違っています。したがって、equals()メソッドをオーバーライドして、独自の実装に置き換える必要があります。 リンク
デザイン自体には価値がありません。デザインは常に目標を達成するために使用されます。ここでの目標は何ですか?コードの驚きの量を減らしたいですか?バグを防ぎたいですか?盲目的にルールに従っていますか?
また、設計には常にコストがかかります。 名前にふさわしいすべてのデザインは、目標の矛盾があることを意味します 。
それを念頭に置いて、次の質問に対する答えを見つける必要があります。
チームに多くのジュニア開発者がいるとします。彼らは、彼らの問題に対する良い解決策をまだ知らないという理由だけで、愚かなことを必死に試みます。クラスをfinalにすると、バグを防ぐことができます(良い)が、コード内のすべての場所でこれらすべてのクラスを変更可能なクラスにコピーするような「賢い」解決策を考え出すこともできます。
一方、どこでも使用された後にクラスfinal
を作成することは非常に困難ですが、後で見つければfinal
クラスをfinal
以外にすることは簡単です。拡張する必要があります。
インターフェイスを適切に使用する場合、インターフェイスを常に使用し、必要に応じて後で変更可能な実装を追加することにより、「この変更可能にする必要がある」という問題を回避できます。
結論:この答えには「最良の」解決策はありません。それはあなたが喜んでいる価格とあなたが支払わなければならないものに依存します。