web-dev-qa-db-ja.com

不変クラス?

どうすればJavaクラスを不変にすることができますか、不変の必要性は何ですか?これを使用する利点はありますか?

81
JavaUser

不変オブジェクトとは?

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

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

一般に、不変オブジェクトは、メンバーを公開せず、セッターを持たないクラスを定義することで作成できます。

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

class ImmutableInt {
  private final int value;

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

  public int getValue() {
    return value;
  }
}

上記の例からわかるように、ImmutableIntの値はオブジェクトがインスタンス化されたときにのみ設定でき、ゲッター(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;
  }
}

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

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

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

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

不変オブジェクトを持つことにより、不変オブジェクトの状態は変わらないので、オブジェクトを見ているすべてのスレッドが同じ状態を確実に見ることができます。

128
coobird

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

18
Fabian Steeg

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

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として宣言する必要があります(オブジェクトの作成後にその値を変更できないようにするため)
  • 取得メソッドを書く取得するすべての変数メンバー
  • セッターメソッドなし

不変クラスは便利です
-スレッドセーフです。
-彼らはまた、あなたのデザインについて何か深いことを表現しています:「これを変更することはできません。」.

6
duffymo

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

  • finalインスタンス属性を使用して再割り当てを回避する
  • クラス内の内容を変更できる操作を許可しないクラスインターフェイスを使用する(ちょうどgettersおよびnosetters

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

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

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

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

  • すべての変数はprivateである必要があります。
  • mutatorメソッド(セッター)は提供されません。
  • クラスを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

3
Kandy

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

不変クラスは

  • キャッシングの目的
  • 同時環境(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;
    }
}

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

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

0
bbgw

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

必要な依存関係が追加されたと仮定して、抽象アクセサーメソッドで抽象クラスを作成します。インターフェイスまたはアノテーション(@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
Justas