不変のクラス?


回答:


135

不変オブジェクトとは何ですか?

不変オブジェクトとは、インスタンス化された後も状態が変化しないオブジェクトです。

オブジェクトを不変にする方法は?

一般に、不変オブジェクトは、メンバーが公開されておらず、セッターも含まれていないクラスを定義することで作成できます。

次のクラスは不変オブジェクトを作成します。

class ImmutableInt {
  private final int value;

  public ImmutableInt(int i) {
    value = i;
  }

  public int getValue() {
    return value;
  }
}

上記の例に見られるように、ImmutableIntcanの値はオブジェクトがインスタンス化されたときにのみ設定でき、ゲッター(getValue)だけを使用することにより、インスタンス化後にオブジェクトの状態を変更することはできません。

ただし、オブジェクトによって参照されるすべてのオブジェクトも不変である必要があることに注意する必要があります。そうしないと、オブジェクトの状態が変更される可能性があります。

たとえば、配列への参照を許可するかArrayList、ゲッターを介して取得できるようにすると、配列またはコレクションを変更することで内部状態を変更できます。

class NotQuiteImmutableList<T> {
  private final List<T> list;

  public NotQuiteImmutableList(List<T> list) {
    // creates a new ArrayList and keeps a reference to it.
    this.list = new ArrayList(list); 
  }

  public List<T> getList() {
    return list;
  }
}

上記のコードの問題は、をArrayList取得してgetList操作できるため、オブジェクト自体の状態が変更されるため、不変ではないことです。

// notQuiteImmutableList contains "a", "b", "c"
List<String> notQuiteImmutableList= new NotQuiteImmutableList(Arrays.asList("a", "b", "c"));

// now the list contains "a", "b", "c", "d" -- this list is mutable.
notQuiteImmutableList.getList().add("d");

この問題を回避する1つの方法は、ゲッターから呼び出されたときに配列またはコレクションのコピーを返すことです。

public List<T> getList() {
  // return a copy of the list so the internal state cannot be altered
  return new ArrayList(list);
}

不変性の利点は何ですか?

不変性の利点は同時実行性にあります。複数のスレッドが同じオブジェクトの状態を変更しようとしている可能性があるため、可変オブジェクトの正確性を維持することは困難です。これにより、一部のスレッドは、そのオブジェクトへの読み取りと書き込みのタイミングに応じて、同じオブジェクトの異なる状態を認識します。オブジェクト。

不変オブジェクトを使用することで、不変オブジェクトの状態が変化しないため、オブジェクトを参照しているすべてのスレッドに同じ状態が表示されるようにすることができます。


5
スレッドセーフ(並行性にとって重要)は、不変性の唯一の利点ではありません。また、オブジェクトの防御コピーを作成する必要がないことを意味し、変更されるはずのないオブジェクトを誤って変更できないため、バグを防ぎます。
Jesper 2010

7
リストのコピーを返す代わりに、リストreturn Collections.unmodifiableList(list);の読み取り専用ビューを返すこともできます。
Jesper 2010

18
クラスも作らなければなりfinalません。それ以外の場合は、setterメソッド(または他の種類の変更メソッドと変更可能フィールド)を使用して拡張できます。
Abhinav Sarkar

6
@AbhinavSarkarサブクラスからアクセスできないためfinal、クラスにprivateフィールドしかない場合は、そうである必要はありません。
icza 2014

3
@iczaクラスはfinalである必要があります。これは、パブリックgetterメソッドがあるため、クラスを拡張してgetterメソッドをオーバーライドしてから、フィールドを変更することができるため、不変ではなくなりました
sunil 2017

19

すでに与えられた答えに加えて、Effective Java、第2版の不変性について読むことをお勧めします。これは、見落としがちな詳細(防御コピーなど)があるためです。さらに、Effective Java 2ndEd。すべてのJava開発者にとって必読です。


3
これは、見るべき正確なリソースです。言及されている利点は、エラーが発生しにくいコードからスレッドセーフまで多岐にわたります。
gpampara 2010

6

次のようにクラスを不変にします。

public final class Immutable
{
    private final String name;

    public Immutable(String name) 
    {
        this.name = name;
    }

    public String getName() { return this.name; } 

    // No setter;
}

Javaクラスを不変にするための要件は次のとおりです。

  • クラスは次のように宣言する必要がありますfinal(子クラスを作成できないようにするため)
  • クラスのメンバーは次のように宣言する必要がありますfinal(オブジェクトの作成後に値を変更できないようにするため)
  • メンバーを取得するために、その中のすべての変数のGetterメソッドを記述します値ます
  • セッターメソッドなし

不変クラスは次の理由で役立ち
ます-スレッドセーフです。
-彼らはまたあなたのデザインについて深い何かを表現しています:「これを変えることはできません。」、それが当てはまるとき、それはまさにあなたが必要とするものです。


5

不変性は、主に2つの方法で実現できます。

  • を使用して finalインスタンス属性を再割り当てを回避する
  • 単にあなたのクラス(ただの内側にあるものを修正することができる任意の操作を許可しないことをクラスインターフェイスを使用してゲッターなしセッターを

不変性の利点は、これらのオブジェクトに対して行うことができる仮定です。

  • 副作用のないルール(関数型プログラミング言語で非常に人気があります)が得られ、オブジェクトがアトミックまたは非アトミックな方法で変更できないことがわかっているため、並行環境でオブジェクトをより簡単に使用できます。多くのスレッドで使用
  • 言語の実装では、これらのオブジェクトを異なる方法で処理し、静的データに使用されるメモリゾーンに配置して、これらのオブジェクトをより高速かつ安全に使用できるようにします(これは文字列のJVM内で発生します)

不変は、同等の副作用がないことと同じではありません。たとえば、不変オブジェクトは、ファイルへのロギングなどの副作用を引き起こす可能性があります。オブジェクトを不変にすることで副作用がなくなると言うのは少し不正確です。
Grundlefleck 2010

1
@Grundleflek、これは髪の毛を裂いているのではないかと思います。コントラクトの一部としてログファイルを変更し、そのログファイルに他のクラスがアクセスできる場合、そのクラスは不変ではありません。ログファイルが他のクラスから隠されていて、クラスのコントラクトの一部ではない場合、クラスは事実上不変であり、実際にはすべての目的と目的で副作用がありません。ウィキペディアの副作用ページの(ソースなしの)紹介には、「...式は、値を生成するだけでなく、状態を変更したり、呼び出し元の関数との相互作用が観察できる場合、副作用があると言われています」と書かれています。
Jeff Axelrod 2011年

3

不変クラスは、インスタンス化された後に値を再割り当てできません。コンストラクターは、そのプライベート変数に値を割り当てます。セッターメソッドが使用できないため、オブジェクトがnullになるまで値を変更できません。

不変であるためには、次の条件を満たす必要があります。

  • すべての変数はプライベートである必要があります。
  • ミューテーターメソッド(セッター)は提供されていません。
  • クラスをfinal(強い不変性)またはメソッドをfinal(週の不変性)にすることで、メソッドのオーバーライドを回避します。
  • 非プリミティブクラスまたは可変クラスが含まれている場合は、深くクローンを作成します。

/**
* Strong immutability - by making class final
*/
public final class TestImmutablity {

// make the variables private
private String Name;

//assign value when the object created
public TestImmutablity(String name) {
this.Name = name;
}

//provide getters to access values
public String getName() {

return this.Name;
}
}

利点:不変オブジェクトには、消滅するまで初期化された値が含まれます。

java-immutable-classes-short-note


2

不変クラスは、作成後にオブジェクトを変更できないです。

不変クラスは

  • キャッシングの目的
  • 並行環境(ThreadSafe)
  • 相続が難しい
  • どの環境でも値を変更することはできません

文字列クラス

コード例

public final class Student {
    private final String name;
    private final String rollNumber;

    public Student(String name, String rollNumber) {
        this.name = name;
        this.rollNumber = rollNumber;
    }

    public String getName() {
        return this.name;
    }

    public String getRollNumber() {
        return this.rollNumber;
    }
}

2

どうすればJavaクラスを不変にすることができますか?

JEP359を搭載したJDK14 +からは、「records」を使用できます。これは、Immutableクラスを作成するための最も簡単で手間のかからない方法です。

レコードクラスは、レコードの説明を提供するレコードと呼ばれる固定されたフィールドセットの、浅く不変で透過的なキャリアです。それぞれを生じさせる提供された値とANを保持するフィールドcomponentsstatecomponentfinalaccessor、値を取得するためのメソッドを生成します。フィールド名とアクセサー名は、コンポーネントの名前と一致します。

不変の長方形を作成する例を考えてみましょう

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");
  }
}

不変性の必要性とは何ですか?これを使用する利点はありますか?

以前に投稿された回答は、不変性の必要性を正当化するのに十分であり、それは長所です


0

不変オブジェクトを作成する別の方法は、Immutables.orgライブラリを使用することです。

必要な依存関係が追加されたと仮定して、抽象アクセサーメソッドを使用して抽象クラスを作成します。インターフェイスまたはアノテーション(@interface)でアノテーションを付けることで、同じことができます。

package info.sample;

import java.util.List;
import java.util.Set;
import org.immutables.value.Value;

@Value.Immutable
public abstract class FoobarValue {
  public abstract int foo();
  public abstract String bar();
  public abstract List<Integer> buz();
  public abstract Set<Long> crux();
}

生成された不変の実装を生成して使用できるようになりました。

package info.sample;

import java.util.List;

public class FoobarValueMain {
  public static void main(String... args) {
    FoobarValue value = ImmutableFoobarValue.builder()
        .foo(2)
        .bar("Bar")
        .addBuz(1, 3, 4)
        .build(); // FoobarValue{foo=2, bar=Bar, buz=[1, 3, 4], crux={}}

    int foo = value.foo(); // 2

    List<Integer> buz = value.buz(); // ImmutableList.of(1, 3, 4)
  }
}

0

不変クラスは、インスタンスを変更できないクラスです。

各インスタンスに含まれるすべての情報はオブジェクトの存続期間中固定されているため、変更を確認することはできません。

不変クラスは、可変クラスよりも設計、実装、および使用が簡単です。

クラスを不変にするには、次の5つのルールに従います。

  1. オブジェクトの状態を変更するメソッドを提供しないでください

  2. クラスを拡張できないことを確認してください。

  3. すべてのフィールドを最終的にします。

  4. すべてのフィールドを非公開にします。

  5. 変更可能なコンポーネントへの排他的アクセスを確保します。

不変オブジェクトは本質的にスレッドセーフです。それらは同期を必要としません。

不変オブジェクトは自由に共有できます。

不変オブジェクトは、他のオブジェクトの優れた構成要素になります


0

@ Jack、クラスに最終フィールドとセッターがあると、クラスが不変になることはありません。finalキーワードは、変数が再割り当てされないようにするだけです。getterメソッドのすべてのフィールドのディープコピーを返す必要があります。これにより、getterメソッドからオブジェクトを取得した後、オブジェクトの内部状態が乱されないようになります。


-1

英語を母国語としない私は、「不変クラス」が「構築されたクラスオブジェクトは不変である」という一般的な解釈が嫌いです。むしろ、私自身、それを「クラスオブジェクト自体は不変である」と解釈する傾向があります。

とはいえ、「不変クラス」は一種の不変オブジェクトです。違いは、メリットが何であるかを答えるときです。私の知る限り/解釈では、不変クラスはそのオブジェクトが実行時の動作を変更するのを防ぎます。


-1

ここでの答えのほとんどは良いものであり、ルールについて言及しているものもありますが、これらのルールに従う必要がある理由と時期を言葉で表現するのは良いことだと思います。だから以下の説明をしています

  • メンバー変数を「final」として宣言する–それらをfinalとして宣言すると、コンパイラーはそれらを初期化するように強制します。デフォルトのコンストラクター、argコンストラクターによって直接初期化できます(以下のサンプルコードを参照)。初期化後は、最終的なものであるため、変更できません。
  • そしてもちろん、これらの最終変数にSetterを使用しようとすると、コンパイラーはエラーをスローします。

    public class ImmutableClassExplored {
    
        public final int a; 
        public final int b;
    
        /* OR  
        Generally we declare all properties as private, but declaring them as public 
        will not cause any issues in our scenario if we make them final     
        public final int a = 109;
        public final int b = 189;
    
         */
        ImmutableClassExplored(){
            this. a = 111;
            this.b = 222;
        }
    
        ImmutableClassExplored(int a, int b){
            this.a = a;
            this.b= b;
        }
    }
    

クラスを「final」として宣言する必要がありますか?

  • クラス宣言にfinalキーワードがないと、クラスを継承できます。したがって、サブクラスはゲッターメソッドをオーバーライドできます。ここでは、2つのシナリオを検討する必要があります。

1.プリミティブメンバーのみを持つ:問題はありませんクラスにプリミティブメンバーしかない場合、クラスをfinalとして宣言する必要はありません。

2.オブジェクトをメンバー変数として持つオブジェクトをメンバー変数として持つ場合、それらのオブジェクトのメンバーも最終的なものにする必要があります。つまり、ツリーの奥深くをトラバースし、すべてのオブジェクト/プリミティブを最終的なものにする必要がありますが、これは常に可能であるとは限りません。したがって、回避策は、継承を防ぐクラスをfinalにすることです。したがって、サブクラスがゲッターメソッドをオーバーライドすることに疑問の余地はありません。


-1

Lombokの@Valueアノテーションを使用して、不変のクラスを生成できます。以下のコードと同じくらい簡単です。

@Value
public class LombokImmutable {
    int id;
    String name;
}

ロンボクのサイトのドキュメントによると:

@Valueは@Dataの不変のバリアントです。デフォルトでは、すべてのフィールドがプライベートおよびファイナルになり、セッターは生成されません。不変性はサブクラスに強制できるものではないため、クラス自体もデフォルトでfinalになります。@Dataと同様に、便利なtoString()、equals()、hashCode()メソッドも生成され、各フィールドはgetterメソッドを取得し、すべての引数(フィールド宣言で初期化される最終フィールドを除く)をカバーするコンストラクターも生成されます。 。

完全に機能する例はここにあります。


受け入れられた回答(緑色の✓を探してください)や他の回答がある古い質問に回答する前に、あなたの回答が何か新しいものを追加するか、そうでなければそれらに関連して役立つことを確認してください。OPの質問に答えるときは注意してください。Javaクラスを不変にするにはどうすればよいですか。不変性の必要性は何ですか。これを使用する利点はありますか。。–サードパーティのフレームワーク/ライブラリであるLombokを使用できるという部分的な回答のみを提供していますが、これは必ずしもOPの質問の内容ではありません。また、Java 15がリリースされましたが、Javaを使用できるのになぜLombokをrecord使用するのですか?
Ivo Mori

私はこれを達成する方法のオプションの1つを与えています。誰もがJava15を使用しているわけではありません。今日のアプリケーションの大部分は以前のバージョンで実行されているため、Lombokを使用してもあまり意味がありません。私の現在のプロジェクトでは、最近Java 11に移行し、Lombokを使用して不変のクラスを実現しています。また、私の答えは、すでに存在していた答えへの追加です。不変のものはすでに回答済みですが、完成のために書き直すことを期待していると思います。
アヌバフ

けっこうだ。私は反対票も賛成票もありません。私は、他の2人がこの回答に反対票を投じた理由を指摘しようとしました。回答を編集するかどうか、またどのように編集するかはあなた次第です。
Ivo Mori
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.