Javaで不変オブジェクトを作成するにはどうすればよいですか?
どのオブジェクトを不変と呼ぶべきですか?
すべての静的メンバーを持つクラスがある場合、それは不変ですか?
Javaで不変オブジェクトを作成するにはどうすればよいですか?
どのオブジェクトを不変と呼ぶべきですか?
すべての静的メンバーを持つクラスがある場合、それは不変ですか?
回答:
以下は、不変オブジェクトの厳しい要件です。
final
が、オブジェクトは変更可能である可能性があります。つまりprivate final Date imStillMutable
)。defensive copies
これらの場合に作成する必要があります。クラスを作る背後にある理由final
は非常に微妙で、見過ごされがちです。最終的な人ではない場合は、クラスを自由に拡張したり、オーバーライドしpublic
たり、protected
動作したり、可変プロパティを追加したりして、代わりにサブクラスを提供できます。クラスfinal
を宣言することで、これが起こらないようにすることができます。
実際の問題を確認するには、以下の例を検討してください。
public class MyApp{
/**
* @param args
*/
public static void main(String[] args){
System.out.println("Hello World!");
OhNoMutable mutable = new OhNoMutable(1, 2);
ImSoImmutable immutable = mutable;
/*
* Ahhhh Prints out 3 just like I always wanted
* and I can rely on this super immutable class
* never changing. So its thread safe and perfect
*/
System.out.println(immutable.add());
/* Some sneak programmer changes a mutable field on the subclass */
mutable.field3=4;
/*
* Ahhh let me just print my immutable
* reference again because I can trust it
* so much.
*
*/
System.out.println(immutable.add());
/* Why is this buggy piece of crap printing 7 and not 3
It couldn't have changed its IMMUTABLE!!!!
*/
}
}
/* This class adheres to all the principles of
* good immutable classes. All the members are private final
* the add() method doesn't modify any state. This class is
* just a thing of beauty. Its only missing one thing
* I didn't declare the class final. Let the chaos ensue
*/
public class ImSoImmutable{
private final int field1;
private final int field2;
public ImSoImmutable(int field1, int field2){
this.field1 = field1;
this.field2 = field2;
}
public int add(){
return field1+field2;
}
}
/*
This class is the problem. The problem is the
overridden method add(). Because it uses a mutable
member it means that I can't guarantee that all instances
of ImSoImmutable are actually immutable.
*/
public class OhNoMutable extends ImSoImmutable{
public int field3 = 0;
public OhNoMutable(int field1, int field2){
super(field1, field2);
}
public int add(){
return super.add()+field3;
}
}
実際には、依存性注入環境で上記の問題が発生することは非常に一般的です。あなたは物事を明示的にインスタンス化しておらず、与えられたスーパークラス参照は実際にはサブクラスである可能性があります。
要点は、不変性について厳しい保証を行うには、クラスをとしてマークする必要があるということfinal
です。これについては、JoshuaBlochのEffectiveJavaで詳しく説明されており、Javaメモリモデルの仕様で明示的に参照されています。
パブリックミューテーター(セッター)メソッドをクラスに追加しないでください。
private
か、クラスはである必要がありますfinal
。継承を避けるためだけに。継承はカプセル化に違反するためです。
クラスは不変ではなく、オブジェクトは不変です。
不変とは、初期化後にパブリックに表示される状態を変更できないことを意味します。
フィールドは最終宣言する必要はありませんが、スレッドセーフを確保するのに非常に役立ちます。
クラスに静的メンバーしかない場合、そのオブジェクトの状態を変更できないため、このクラスのオブジェクトは不変です(おそらく作成することもできません:))
Javaでクラスを不変にするために、次の点に注意して
ください。1。クラスのインスタンス変数の値を変更するsetterメソッドを提供しないでください。
2.クラスを「final」として宣言します。これにより、他のクラスがそれを拡張できなくなり、インスタンス変数値を変更する可能性のあるメソッドがオーバーライドされなくなります。
3.インスタンス変数をprivateおよびfinalとして宣言します。
4.クラスのコンストラクターをプライベートとして宣言し、必要に応じてファクトリメソッドを追加してクラスのインスタンスを作成することもできます。
これらのポイントが役立つはずです!!
private
でしょうか。
Oracleサイトから、Javaで不変オブジェクトを作成する方法。
- 「セッター」メソッド(フィールドまたはフィールドによって参照されるオブジェクトを変更するメソッド)を提供しないでください。
- すべてのフィールドをファイナルでプライベートにします。
- サブクラスがメソッドをオーバーライドすることを許可しないでください。これを行う最も簡単な方法は、クラスをfinalとして宣言することです。より洗練されたアプローチは、コンストラクターをプライベートにし、ファクトリメソッドでインスタンスを構築することです。
- インスタンスフィールドに可変オブジェクトへの参照が含まれている場合は、それらのオブジェクトを変更できないようにします
。I。可変オブジェクトを変更するメソッドを提供しないでください。
II。可変オブジェクトへの参照を共有しないでください。コンストラクターに渡された外部の可変オブジェクトへの参照は絶対に保存しないでください。必要に応じて、コピーを作成し、コピーへの参照を保存します。同様に、メソッドで元のオブジェクトが返されないようにするために、必要に応じて内部の可変オブジェクトのコピーを作成します。
不変オブジェクトは、作成後に内部状態を変更しないオブジェクトです。同期せずにスレッド間で共有できるため、マルチスレッドアプリケーションで非常に役立ちます。
1.セッターメソッドを追加しないでください
不変オブジェクトを構築している場合、その内部状態は決して変化しません。セッターメソッドのタスクは、フィールドの内部値を変更することであるため、追加することはできません。
2.すべてのフィールドをファイナルおよびプライベートとして宣言します
プライベートフィールドはクラスの外部からは表示されないため、手動で変更を適用することはできません。
フィールドfinalを宣言すると、プリミティブ値を参照している場合、オブジェクトを参照している場合は値が変更されないことが保証されます。参照は変更できません。 これは、プライベート最終フィールドのみを持つオブジェクトが変更可能でないことを保証するのに十分ではありません。
3.フィールドが可変オブジェクトの場合、getterメソッド用にそのフィールドの防御コピーを作成します
フィールドのfinalとprivateを定義するだけでは、その内部状態を変更できるため、これまで見てきました。 この問題を解決するには、そのフィールドの防御コピーを作成し、要求されるたびにそのフィールドを返す必要があります。
4.コンストラクターに渡された可変オブジェクトをフィールドに割り当てる必要がある場合は、その防御コピーを作成します
コンストラクターに渡された参照を保持している場合も、変更できるため、同じ問題が発生します。したがって、コンストラクターに渡されたオブジェクトへの参照を保持すると、可変オブジェクトを作成できます。この問題を解決するには、パラメーターが可変オブジェクトである場合、パラメーターの防御コピーを作成する必要があります。
フィールドが不変オブジェクトへの参照である場合、コンストラクターとゲッターメソッドでフィールドの防御コピーを作成する必要はなく、フィールドをfinalおよびprivateとして定義するだけで十分であることに注意してください。
5.サブクラスがメソッドをオーバーライドすることを許可しない
サブクラスがメソッドをオーバーライドする場合、サブクラスは、その防御コピーではなく、可変フィールドの元の値を返すことができます。
これらの単純なルールに従うと、スレッドセーフであるため、スレッド間で不変オブジェクトを自由に共有できます。
正しいことも悪いこともありません、それはあなたが好むものに依存します。それはあなたの好みとあなたが達成したいことによって異なります(そして、どちらかの側の熱狂的なファンを遠ざけることなく両方のアプローチを簡単に使用できることは、いくつかの言語が求めている聖杯です)。
まず、不変オブジェクトを作成する必要がある理由と、不変オブジェクトの利点は何ですか。
不変オブジェクトの利点
並行性とマルチスレッドそれは自動的にスレッドセーフなので同期の問題....など
コンストラクターをコピーする必要はありません。クローン を実装する必要はありません。 クラスをオーバーライドすることはできません フィールドをプライベートおよび最後 の呼び出し元として、引数なしのコンストラクターを使用する代わりに、単一のステップでオブジェクトを完全に構築するように強制します
不変オブジェクトは、その状態が不変オブジェクトの構築後にオブジェクトのデータを変更できないことを意味する単なるオブジェクトです。
以下のコードを参照してください。
public final class ImmutableReminder{
private final Date remindingDate;
public ImmutableReminder (Date remindingDate) {
if(remindingDate.getTime() < System.currentTimeMillis()){
throw new IllegalArgumentException("Can not set reminder" +
" for past time: " + remindingDate);
}
this.remindingDate = new Date(remindingDate.getTime());
}
public Date getRemindingDate() {
return (Date) remindingDate.clone();
}
}
可変性を最小限に抑える
不変クラスは、インスタンスを変更できないクラスです。各インスタンスに含まれるすべての情報は、インスタンスが作成されたときに提供され、オブジェクトの存続期間中固定されます。
JDK不変クラス:文字列、ボックス化されたプリミティブクラス(ラッパークラス)、BigInteger、BigDecimalなど。
クラスを不変にする方法は?
防御的なコピーを作成します。変更可能なコンポーネントへの排他的アクセスを確保します。
public List getList(){return Collections.unmodizableList(list); <===呼び出し元に返す前の可変フィールドの防御コピー}
クラスに可変オブジェクトを参照するフィールドがある場合は、クラスのクライアントがこれらのオブジェクトへの参照を取得できないようにしてください。このようなフィールドをクライアント提供のオブジェクト参照に初期化したり、アクセサからオブジェクト参照を返したりしないでください。
import java.util.Date;
public final class ImmutableClass {
public ImmutableClass(int id, String name, Date doj) {
this.id = id;
this.name = name;
this.doj = doj;
}
private final int id;
private final String name;
private final Date doj;
public int getId() {
return id;
}
public String getName() {
return name;
}
/**
* Date class is mutable so we need a little care here.
* We should not return the reference of original instance variable.
* Instead a new Date object, with content copied to it, should be returned.
* */
public Date getDoj() {
return new Date(doj.getTime()); // For mutable fields
}
}
import java.util.Date;
public class TestImmutable {
public static void main(String[] args) {
String name = "raj";
int id = 1;
Date doj = new Date();
ImmutableClass class1 = new ImmutableClass(id, name, doj);
ImmutableClass class2 = new ImmutableClass(id, name, doj);
// every time will get a new reference for same object. Modification in reference will not affect the immutability because it is temporary reference.
Date date = class1.getDoj();
date.setTime(date.getTime()+122435);
System.out.println(class1.getDoj()==class2.getDoj());
}
}
詳細については、私のブログを参照してください:http:
//javaexplorer03.blogspot.in/2015/07/minimize-mutability.html
不変オブジェクトは、作成後に状態を変更できないオブジェクトです。たとえば、Stringクラスは不変クラスです。不変オブジェクトは変更できないため、同時実行でもスレッドセーフです。
不変クラスの機能:
不変クラスを作成するためのキー:
クラスを不変クラスにする場合は、次のいくつかの手順を検討する必要があります。
上で入力した内容を一目見てみましょう。
//ImmutableClass
package younus.attari;
public final class ImmutableExample {
private final String name;
private final String address;
public ImmutableExample(String name,String address){
this.name=name;
this.address=address;
}
public String getName() {
return name;
}
public String getAddress() {
return address;
}
}
//MainClass from where an ImmutableClass will be called
package younus.attari;
public class MainClass {
public static void main(String[] args) {
ImmutableExample example=new ImmutableExample("Muhammed", "Hyderabad");
System.out.println(example.getName());
}
}
@ nsfyn55によって提供された回答に加えて、オブジェクトの不変性についても次の側面を考慮する必要があります。これらは最も重要です。
次のクラスを検討してください。
public final class ImmutableClass {
private final MutableClass mc;
public ImmutableClass(MutableClass mc) {
this.mc = mc;
}
public MutableClass getMutClass() {
return this.mc;
}
}
public class MutableClass {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
public class MutabilityCheck {
public static void main(String[] args) {
MutableClass mc = new MutableClass();
mc.setName("Foo");
ImmutableClass iMC = new ImmutableClass(mc);
System.out.println(iMC.getMutClass().getName());
mc.setName("Bar");
System.out.println(iMC.getMutClass().getName());
}
}
以下は、MutabilityCheckからの出力です。
Foo
Bar
注意することが重要です、
'copying'または'cloing'のいずれかによって、(コンストラクターを介して)不変オブジェクト上に可変オブジェクトを構築する次の変更によって記述される不変のインスタンス変数にするします。
public final class ImmutableClass {
private final MutableClass mc;
public ImmutableClass(MutableClass mc) {
this.mc = new MutableClass(mc);
}
public MutableClass getMutClass() {
return this.mc;
}
}
public class MutableClass {
private String name;
public MutableClass() {
}
//copy constructor
public MutableClass(MutableClass mc) {
this.name = mc.getName();
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
以下はMutabilityCheckクラスから引き続き有効であるため、完全な不変性は保証されません。
iMC.getMutClass().setName("Blaa");
ただし、1。で行った変更を使用してMutabilityCheckを実行すると、出力は次のようになります。
Foo
Foo
オブジェクトで完全な不変性を実現するには、そのすべての依存オブジェクトも不変である必要があります
JEP359を搭載したJDK14 +から、「records
」。これは、Immutableクラスを作成するための最も簡単で手間のかからない方法です。
レコードクラスは、レコードの説明を提供するレコードと呼ばれる固定されたフィールドセットの、浅く不変で透過的なキャリアです。それぞれが、提供された値を保持するフィールドと、値を取得するためのメソッドを生成します。フィールド名とアクセサー名は、コンポーネントの名前と一致します。components
state
component
final
accessor
不変の長方形を作成する例を考えてみましょう
record Rectangle(double length, double width) {}
コンストラクターを宣言する必要はなく、equals
&hashCode
メソッドを実装する必要もありません。すべてのレコードには、名前と状態の説明が必要です。
var rectangle = new Rectangle(7.1, 8.9);
System.out.print(rectangle.length()); // prints 7.1
オブジェクトの作成中に値を検証する場合は、コンストラクターを明示的に宣言する必要があります。
public Rectangle {
if (length <= 0.0) {
throw new IllegalArgumentException();
}
}
レコードの本体は、静的メソッド、静的フィールド、静的初期化子、コンストラクター、インスタンスメソッド、およびネストされた型を宣言できます。
インスタンスメソッド
record Rectangle(double length, double width) {
public double area() {
return this.length * this.width;
}
}
静的フィールド、メソッド
状態はコンポーネントの一部である必要があるため、インスタンスフィールドをレコードに追加することはできません。ただし、静的フィールドとメソッドを追加することはできます。
record Rectangle(double length, double width) {
static double aStaticField;
static void aStaticMethod() {
System.out.println("Hello Static");
}
}