0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Effective Java 4-17:可変性を最小限をする

Posted at

Effective Java 4-17:可変性を最小限をする

要点

可能なかぎり不変(immutable)にし、どうしても変更が必要な箇所は局所化して可変性の範囲(スコープ・API)を最小化する。

なぜ可変性を最小化するのか

  • バグが減る: 状態変化が少ないほど思考負荷が下がり、誤った状態遷移によるバグが減る。
  • スレッド安全が簡単: 不変オブジェクトはスレッド間で安全に共有できる(ロック不要)。
  • 設計が安定する: 不変なら内部表現を自由に変えられる(カプセル化が強化される)。
  • テストしやすい: 入力→出力が一定なので単体テストが単純化される。

不変クラスを作るための「ルール」(必須チェックリスト)

  • クラスを final にする(サブクラス化を禁止)。
  • すべてのフィールドを private final にする。
  • this の参照を公開しない(this を公開フィールド・コールバックで渡さない)。
  • 可変オブジェクトをフィールドに持つときは防御的コピーを行う(コンストラクタでコピー、getter でコピー or 不変ビューを返す)。
  • メソッドは状態を変更するものを持たない(setter を持たない)。
  • equals/hashCode/toString を適切に実装する(不変ならハッシュキャッシュが可能)。
  • シリアライズ可能にするなら readResolve 等で不変性を保つ配慮をする。

悪い例

public class BadPerson {
    public String name;      // public で直接書き換え可能
    public int[] tags;       // 配列参照をそのまま保持 -> 共有される
}

良い例

public final class Person {
    private final String name;
    private final int[] tags; // 内部は不変に見せるために defensive copy

    public Person(String name, int[] tags) {
        this.name = Objects.requireNonNull(name);
        this.tags = (tags == null) ? null : tags.clone(); // 防御的コピー
    }

    public String getName() { return name; }

    public int[] getTags() {
        return (tags == null) ? null : tags.clone(); // コピーを返す -> 内部は守られる
    }
}

配列/コレクションを扱うときの注意

  • フィールドに List や Map を持つなら、コンストラクタでコピーし(new ArrayList<>(incoming))、公開時は不変ビューを返す(Collections.unmodifiableList(...))。
  • Collections.unmodifiableList は内部参照が変更されたら見た目も変わる点に注意(完全な防御にはコピー + unmodifiable を組合せる)。
this.items = Collections.unmodifiableList(new ArrayList<>(items));
public List<Item> getItems() { return items; } // 安全

フィールド数が多い/複雑な構築は Builder を使う

不変かつ多フィールドのクラスは Builder パターン を使うと可読性と安全性が両立します。

public final class Complex {
    private final String a;
    private final int b;
    private final List<String> tags;

    private Complex(Builder b) {
        this.a = b.a;
        this.b = b.b;
        this.tags = Collections.unmodifiableList(new ArrayList<>(b.tags));
    }

    public static class Builder {
        private String a;
        private int b;
        private List<String> tags = new ArrayList<>();
        public Builder a(String a){ this.a = a; return this; }
        public Builder b(int b){ this.b = b; return this; }
        public Builder addTag(String t){ tags.add(t); return this; }
        public Complex build(){ return new Complex(this); }
    }
}

ハッシュコードのキャッシュ(不変クラスの利点)

不変なら hashCode() を一度計算してキャッシュできる(高頻度でハッシュされる場合に有効):

private final int cachedHash;
public MyImmutable(...) {
    // assign fields
    cachedHash = computeHash();
}
@Override public int hashCode() { return cachedHash; }

まとめ

  • まず不変にできないか考える。ほとんどのクラスは不変にできるか、少なくとも可変性を局所化できる。
  • 不変にすればバグが減り、スレッド安全で、将来の改変が容易になる。
  • 可変であるならば、その範囲と公開 API を最小化して、外部に mutable な参照を漏らさないこと。
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?