web-dev-qa-db-ja.com

Javaで不変オブジェクトを作成する方法は?

Javaで不変オブジェクトを作成する方法は?

どのオブジェクトを不変と呼ぶべきですか?

すべての静的メンバーを持つクラスがある場合、それは不変ですか?

80
Neel Salpe

以下は、不変オブジェクトのhard要件です。

  1. クラスを最終決定する
  2. すべてのメンバーを最終的なものにし、静的ブロックまたはコンストラクターで明示的に設定します
  3. すべてのメンバーを非公開にする
  4. 状態を変更するメソッドはありません
  5. 可変メンバーへのアクセスを制限するように細心の注意を払ってください(フィールドは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としてマークする必要があることです。これは、Joshua Blochの Effective Java で詳しく説明されており、 Javaメモリモデル の仕様で明示的に参照されています。

82
nsfyn55

パブリックミューテーター(セッター)メソッドをクラスに追加しないでください。

14
BalusC

クラスは不変ではなく、オブジェクトは不変です。

不変という意味:初期化後、公開されている公開状態は変更できません。

フィールドを最終宣言する必要はありませんが、スレッドの安全性を確保するのに非常に役立ちます

クラスに静的メンバーのみがある場合、このクラスのオブジェクトは不変です。そのオブジェクトの状態を変更できないためです(おそらく作成もできません:))

13

クラスをJavaで不変にするには、次の点に注意してください。

1。クラスのインスタンス変数の値を変更するセッターメソッドを提供しないでください。

2。クラスを宣言します '最後の' 。これにより、他のクラスがクラスを拡張できなくなり、インスタンス変数値を変更する可能性のあるメソッドをオーバーライドできなくなります。

3。インスタンス変数を次のように宣言します プライベートおよびファイナル

4。クラスのコンストラクタを次のように宣言することもできます 民間 必要に応じて、ファクトリメソッドを追加してクラスのインスタンスを作成します。

これらのポイントが役立つはずです!!

6
VishEnthusiast

Oracle サイトから、Javaで不変オブジェクトを作成する方法。

  1. 「セッター」メソッド(フィールドによって参照されるフィールドまたはオブジェクトを変更するメソッド)を提供しないでください。
  2. すべてのフィールドを最終および非公開にします。
  3. サブクラスによるメソッドのオーバーライドを許可しないでください。これを行う最も簡単な方法は、クラスをfinalとして宣言することです。より洗練されたアプローチは、コンストラクタをプライベートにし、ファクトリメソッドでインスタンスを構築することです。
  4. インスタンスフィールドに可変オブジェクトへの参照が含まれる場合、それらのオブジェクトの変更を許可しないでください。
    私。可変オブジェクトを変更するメソッドを提供しないでください。
    II。可変オブジェクトへの参照を共有しないでください。コンストラクタに渡される外部の可変オブジェクトへの参照を保存しないでください。必要に応じて、コピーを作成し、コピーへの参照を保存します。同様に、メソッド内で元のオブジェクトが返されないように、必要に応じて内部可変オブジェクトのコピーを作成します。
3
e11438

可変性を最小限に抑える

不変クラスは、インスタンスを変更できないクラスです。各インスタンスに含まれるすべての情報は、作成時に提供され、オブジェクトの存続期間中は固定されます。

JDK不変クラス:文字列、ボックス化されたプリミティブクラス(ラッパークラス)、BigInteger、BigDecimalなど。

クラスを不変にする方法は?

  1. オブジェクトの状態を変更するメソッド(ミューテーター)を提供しないでください。
  2. クラスを拡張できないことを確認してください。
  3. すべてのフィールドを最終決定します。
  4. すべてのフィールドをプライベートにします。これにより、クライアントはフィールドによって参照される可変オブジェクトへのアクセスを取得し、これらのオブジェクトを直接変更できなくなります。
  5. 防御的なコピーを作成します。可変コンポーネントへの排他的アクセスを確保してください。

    public List getList(){Collections.unmodifiableList(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

2
Rajesh Dixit

まず、不変オブジェクトを作成する必要がある理由と、不変オブジェクトの利点を知っています。

不変オブジェクトの利点

同時実行性とマルチスレッド化自動的にスレッドセーフになるため、同期の問題が発生します。

copy constructorを実装する必要はありませんclone。クラスをoverrideにすることはできませんprivate and final引数なしのコンストラクターを使用する代わりに、呼び出し側に単一ステップでオブジェクトを完全に構築させる

不変オブジェクトは、不変オブジェクトが構築された後にオブジェクトのデータが変更できない状態を意味する単純なオブジェクトです。

以下のコードを参照してください。

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();
    }
}
2
J.B.Vala
  • 「セッター」メソッド(フィールドによって参照されるフィールドまたはオブジェクトを変更するメソッド)を提供しないでください。
  • すべてのフィールドを最終および非公開にします。
  • サブクラスによるメソッドのオーバーライドを許可しないでください。これを行う最も簡単な方法は、クラスをfinalとして宣言することです。より洗練されたアプローチは、コンストラクタをプライベートにし、ファクトリメソッドでインスタンスを構築することです。
  • インスタンスフィールドに変更可能なオブジェクトへの参照が含まれる場合、それらのオブジェクトの変更を許可しないでください。
    • 可変オブジェクトを変更するメソッドを提供しないでください。
    • 可変オブジェクトへの参照を共有しないでください。コンストラクタに渡される外部の可変オブジェクトへの参照を保存しないでください。必要に応じて、コピーを作成し、コピーへの参照を保存します。同様に、メソッド内で元のオブジェクトが返されないように、必要に応じて内部可変オブジェクトのコピーを作成します。
2
Ajay Kumar

不変オブジェクトとは、作成後に内部状態を変更しないオブジェクトです。同期せずにスレッド間で共有できるため、マルチスレッドアプリケーションで非常に便利です。

不変オブジェクトを作成するには、いくつかの簡単なルールに従う必要があります。

1。セッターメソッドを追加しないでください

不変オブジェクトを構築している場合、その内部状態は決して変化しません。セッターメソッドのタスクは、フィールドの内部値を変更することです。したがって、フィールドを追加することはできません。

2。すべてのフィールドをfinalおよびprivateとして宣言

プライベートフィールドはクラスの外部からは見えないため、手動で変更することはできません。

フィールドfinalを宣言すると、プリミティブ値を参照する場合、オブジェクトを参照する場合、値は変更されないことが保証されます。 これは、プライベート最終フィールドのみを持つオブジェクトが変更可能でないことを保証するのに十分ではありません。

。フィールドが可変オブジェクトである場合、getterメソッド用にそのコピーを作成します

フィールドのfinalとprivateを定義するだけでは、内部状態を変更できるため十分ではないことを以前に見てきました。 この問題を解決するには、そのフィールドの防御コピーを作成し、要求されるたびにそのフィールドを返す必要があります。

4。コンストラクターに渡される可変オブジェクトをフィールドに割り当てる必要がある場合は、その防御コピーを作成します

コンストラクターに渡された参照を保持する場合、変更できるため、同じ問題が発生します。したがって、コンストラクターに渡されたオブジェクトへの参照を保持すると、可変オブジェクトを作成できます。この問題を解決するには、パラメーターが可変オブジェクトである場合、パラメーターの防御コピーを作成する必要があります。

フィールドが不変オブジェクトへの参照である場合、コンストラクターおよびgetterメソッドでそのオブジェクトの防御コピーを作成する必要はありませんが、フィールドをfinalおよびprivateとして定義するだけで十分です。

5。サブクラスによるメソッドのオーバーライドを許可しない

サブクラスがメソッドをオーバーライドすると、可変フィールドの防御的なコピーの代わりに、可変フィールドの元の値を返すことができます。

この問題を解決するには、次のいずれかを実行できます。

  1. 不変クラスをfinalとして宣言して、拡張できないようにします
  2. 不変クラスfinalのすべてのメソッドを宣言して、オーバーライドできないようにします
  3. プライベートコンストラクターを持つクラスは拡張できないため、プライベートコンストラクターとファクトリーを作成して不変クラスのインスタンスを作成する

これらの単純なルールに従うと、スレッドセーフであるため、スレッド間で不変オブジェクトを自由に共有できます!

以下に注目すべき点をいくつか示します。

  • 多くの場合、不変オブジェクトは確かに人生を単純にします。それらは、値の型に特に適用できます。オブジェクトにはIDがないため、簡単に置き換えることができ、並行プログラミングをより安全かつクリーンにすることができます(スレッド間で共有される可変状態)ただし、大規模および/または複雑なオブジェクトの場合、変更ごとにオブジェクトの新しいコピーを作成すると、非常にコストがかかり、退屈になります。また、異なるIDを持つオブジェクトの場合、既存のオブジェクトの変更は、変更された新しいコピーを作成するよりもはるかに簡単で直感的です。
  • 双方向の関係のように、不変オブジェクトでは単純にできないことがいくつかあります。 1つのオブジェクトに関連付け値を設定すると、そのIDが変更されます。したがって、他のオブジェクトに新しい値を設定すると、それも変更されます。問題は、参照を持つオブジェクトを表す新しいインスタンスが作成されたため、最初のオブジェクトの参照が無効になることです。これを続けると、無限の回帰になります。
  • バイナリ検索ツリーを実装するには、毎回新しいツリーを返す必要があります:新しいツリーでは、変更された各ノードのコピーを作成する必要があります(変更されていないブランチは共有されます) 。挿入関数の場合、これはそれほど悪くはありませんが、私にとっては、削除と再バランスの作業を始めたとき、物事はすぐにかなり非効率になりました。
  • HibernateおよびJPAは、システムが変更可能なオブジェクトを使用することを本質的に指示します。それらの前提は、データオブジェクトの変更を検出して保存することであるためです。
  • 言語に応じて、コンパイラは不変データを処理する際に、データが決して変更されないことを知っているため、多くの最適化を実行できます。あらゆる種類のものがスキップされるため、パフォーマンスが大幅に向上します。
  • 他の既知のJVM言語(Scala、Clojure)を見ると、コード内で変更可能なオブジェクトはめったに見られないため、シングルスレッドでは不十分なシナリオでそれらを使用し始めます。

正しいか間違っているかはありません。あなたが好むものに依存します。それはあなたの好みと達成したいものに依存します(そして、一方の熱心なファンを疎外することなく両方のアプローチを簡単に使用できることは、いくつかの言語が求めている聖杯です)。

2
amitkumar12788

クラスを不変クラスとして使用する場合は、次のいくつかの手順を考慮する必要があります。

  1. クラスは最終としてマークする必要があります
  2. すべてのフィールドはプライベートで最終でなければなりません
  3. セッターをコンストラクターに置き換えます(値を変数に割り当てるため)。

上記で入力した内容を見てみましょう。

//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());

    }
}
1
user3205589

オブジェクトは、作成後に状態を変更できない場合、不変と呼ばれます。 Javaで不変クラスを作成する最も簡単な方法の1つは、すべてのフィールドをfinalに設定することです。「Java.util.Date」などの可変クラスを含む不変クラスを記述する必要がある場合。そのような場合に不変性を保持するために、元のオブジェクトのコピーを返すことをお勧めします。

1
Rudra

不変オブジェクトは、作成後は状態を変更できないオブジェクトです。たとえば、Stringクラスは不変クラスです。不変オブジェクトは変更できないため、同時実行でもスレッドセーフです。

不変クラスの機能:

  • 構築が簡単
  • 自動的にスレッドセーフ
  • mapキーとSetの適切な候補は、処理中に内部状態が変更されないためです
  • 常に同じ状態を表すため、クローンを実装する必要はありません

不変クラスを作成するためのキー:

  • クラスをオーバーライドできないことを確認してください
  • すべてのメンバー変数をプライベートおよび最終にする
  • セッターメソッドを与えない
  • 構築段階でオブジェクト参照をリークしないでください
1
Manjul

一般的に無視されますが、不変オブジェクトの重要なプロパティ

@ nsfyn55が提供する回答に加えて、オブジェクトの不変性については、次の側面も考慮する必要があります。これらはprime重要です

次のクラスを検討してください。

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

注意することが重要です、

  1. '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. ただし、1で行った変更でMutabilityCheckを実行すると、出力は次のようになります。

    Foo
    Foo
    
  2. オブジェクトで完全な不変性を実現するには、そのすべての依存オブジェクトも不変でなければなりません

0
KJ Sudarshan