クラスを不変にするときに、クラスの前にfinalキーワードが必要な理由を明確にしてください 。つまり、すべての属性を private および final として宣言すると、それも不変クラスになりますね。
質問が簡単に思える場合は申し訳ありませんが、私はそれについて本当に混乱しています。手伝ってください。
編集済み: final と宣言されたクラスをサブクラス化できないことは知っています..しかし、各属性が非公開で最終的な場合、どのような違いがありますか?
クラスを不変にするときに、クラスの前にfinalキーワードが必要な理由を明確にしてください 。つまり、すべての属性を private および final として宣言すると、それも不変クラスになりますね。
質問が簡単に思える場合は申し訳ありませんが、私はそれについて本当に混乱しています。手伝ってください。
編集済み: final と宣言されたクラスをサブクラス化できないことは知っています..しかし、各属性が非公開で最終的な場合、どのような違いがありますか?
スタッカーが言うようにfinal
、クラスがサブクラス化されていないことを確認してください。これは、その不変性に依存しているコードが安全に実行できるようにするために重要です。
たとえば、不変型 (各フィールドも不変型) は、データ競合などを心配することなく、スレッド間で自由に使用できます。
public class Person {
private final String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
インスタンスをスレッド間で問題なく自由に共有できるようです。しかし、共有しているオブジェクトが実際に変更可能なサブクラスPerson
である場合はどうでしょう。
public class Employee extends Person {
private String company;
public Employee(String name, String company) {
super(name);
this.company = company;
}
public void setCompany(String company) {
this.company = company;
}
public String getCompany() {
return company;
}
}
のインスタンスは不変ではEmployee
ないため、スレッド間で安全に共有できません。しかし、共有を行うコードは、それらを ... のインスタンスとしてしか認識していない可能性がありPerson
、それらを誤った安心感に導く可能性があります。
同じことがキャッシングにも当てはまります。不変型をキャッシュして再利用しても安全ですよね? 確かに、純粋に不変型のインスタンスをキャッシュすることは安全ですが、それ自体は変更を許可しないが、サブクラスを許可する型を扱っている場合、突然安全ではなくなります。
について考えてみてくださいjava.lang.Object
。可変フィールドはありませんが、すべてObject
の参照を不変型への参照であるかのように扱うのは明らかに悪い考えです。基本的に、不変性を型のプロパティと考えるか、オブジェクトのプロパティと考えるかによって異なります。真に不変な型は、「この型の参照が表示されるたびに、それを不変として扱うことができる」と宣言しますが、任意のサブクラス化を許可する型はその主張を行うことができません。
余談ですが、中途半端な方法があります。サブクラス化を「信頼できる」場所だけに制限できれば、すべてが不変であることを保証できますが、それでもそのサブクラス化は許可されます。Java でのアクセスはそれを難しくしますが、たとえば C# では、同じアセンブリ内でのみサブクラス化を許可するパブリック クラスを作成できます。これにより、ポリモーフィズムの利点を考慮しながら、不変性の点で優れた強力なパブリック API を提供できます。 .
final と宣言されたクラスは、サブクラス化できません。http://docs.oracle.com/javase/tutorial/java/IandI/final.htmlも参照してください。
final キーワードのすべての使用の異なるセマンティクスは、Java 言語仕様で説明されています。
不変クラスを作成するために厳密に finalは必要ありません。つまり、最終的でなくても不変クラスを作成できます。
ただし、最終版にしないと、誰かがクラスを拡張し、変更可能なサブクラスを作成する可能性があります (新しい変更可能なフィールドを追加するか、メソッドの保護されたフィールドを変更できるようにメソッドをオーバーライドすることにより)。元の不変クラス)。これは潜在的な問題です。これは、すべてのサブタイプによって不変のプロパティが保持されることを期待するという意味で、Liskov Substitution Principleに違反しています。
したがって、このリスクを回避するために、通常は不変クラスを final にすることをお勧めします。
キーワードの名前が示すように、'final' は、final キーワードが関連付けられている属性が (値に関して) 変更できないことを意味します。つまり、定数のように動作します。
あなたの質問によると、クラスのすべてのメンバーがプライベートかつ最終的なものにされているが、クラスが最終的にされていない場合、同じクラスを継承できますが、最終的なキーワードがそれらに添付されているため、スーパークラスのメンバーは不変です。
すべてのパブリックメソッドとプロテクトメソッドがfinalであり、いずれもプライベートフィールドの変更を許可せず、すべてのパブリックフィールドとプロテクトフィールドがfinalで不変である場合、クラスは半不変、または一種の定数であると言えます。
ただし、サブクラスを作成し、equalsとハッシュコードをオーバーライドする必要がある場合は問題が発生します。そして、あなたがそれらを最終的にしたので、それはできません...それで、すべてが壊れているので、プログラマーが誤ってばかになるのを防ぐために、クラス全体を最終的にしてください。
この種のろくでなしバージョンの不変性を行う代わりに、いくつかのオプションがあります。
不変のインスタンスに追加のデータを添付する場合は、を使用しますMap
。名前に年齢を追加したい場合のように、あなたはしませんclass NameAge extends String
... :-)
メソッドを追加する場合は、静的効用関数のクラスを作成します。これは少し厄介ですが、現在のJavaの方法です。たとえば、ApacheCommonsはそのようなクラスでいっぱいです。
メソッドとデータを追加する場合は、不変クラスのメソッドへのデリゲートメソッドを使用してラッパークラスを作成します。new MyWrapper(myImmutableObj)
追加のメソッドを使用する必要がある人は、とにかくそれらを認識する必要があります。派生した不変のクラスにキャストすることや、多くのユースケースのように行うことには、実際的な違いはあまりありません。
元の可換オブジェクトへの参照が本当に必要であるが(既存のクラスに格納するなど、変更できない)、どこかに追加のデータが必要な場合は、このMap
アプローチを使用して追加のデータを保持する必要があります。
不変オブジェクトとは、状態が存続期間全体にわたって同一であることが保証されているオブジェクトです。final なしで不変性を実装することは完全に可能ですが、それを使用すると、人間 (ソフトウェア開発者) とマシン (コンパイラ) に対してその目的が明確になります。
不変オブジェクトには、非常に望ましい特性がいくつかあります。
they are simple to understand and easy to use
they are inherently thread-safe: they require no synchronization
they make great building blocks for other objects
明らかに final は、不変オブジェクトを定義するのに役立ちます。最初に、オブジェクトを不変としてラベル付けします。これにより、他のプログラマーが簡単に使用および理解できるようになります。2 つ目は、オブジェクトの状態が変更されないことを保証することです。これにより、スレッド セーフなプロパティが有効になります。スレッドの同時実行の問題は、あるスレッドがデータを変更しているときに別のスレッドが同じデータを読み取っている場合に関係します。不変オブジェクトはそのデータを変更しないため、アクセスを同期する必要はありません。
次の条件をすべて満たして、不変クラスを作成します。
Declare all fields private final.
Set all fields in the constructor.
Don't provide any methods that modify the state of the object; provide only getter methods (no setters).
Declare the class final, so that no methods may be overridden.
Ensure exclusive access to any mutable components, e.g. by returning copies.
final として宣言されたクラスはサブクラス化できません。他のクラスは最終クラスを拡張できません。これは、セキュリティとスレッド セーフにいくらかの利点をもたらします。