読者です 読者をやめる 読者になる 読者になる

Effective Javaを読んでいる⑮

不変なクラスが素敵である理由

  • 設計しやすい
  • 実装しやすい
  • バグがでにくい

不変なクラスを作るためのルール

  1. オブジェクトの状態を変えるメソッドを作らない。
  2. 継承できないようにする。
  3. すべてのフィールドをfinalにする。
  4. すべてのフィールドをprivateにする。
  5. 変更可能なコンポーネントへのアクセスを占有する

functional approach

public class Rat {
    public Rat(boolean isDead, String name) {
        super();
        this.isDead = isDead;
        this.name = name;
    }
    public boolean isDead() {
        return isDead;
    }
    public String getName() {
        return name;
    }
    
    private boolean isDead;
    private String name;
    
    public Rat kill(){
        if(isDead){
            throw new AlreadyDeadException("This rat was already dead...");
        }
        return new Rat(true, this.name);
    }
}

そのRatのインスタンスを殺さずに別の死んだRatのインスタンスを返却する。Killではないな・・。

不変クラスにcloneとかcopyコンストラクタはいらない

コピーしないでそのまま使ったらいいてゆう

staticファクトリはなまえをつけられるから素敵

不変クラスのデメリット

状態ごとにインスタンスを生成しないといけないから生成コストがかかる

継承できないようにするには

パターン1. 不変クラスをfinalとして定義する
パターン2. コンストラクタをprivateとかpackage-privateとかにしてpublicなstaticファクトリーを作る
パターン1はまったく拡張できないけどパターン2は同一パッケージなら拡張できて嬉しい f:id:inabajunmr:20160103131036j:plain ヤッター!

なんで継承できちゃうとヤバイのか

よくわからん
サブクラスがうんちだとアレだからそれを防ぐためみたいな感じだと思うけど
f:id:inabajunmr:20160103131311j:plain 不変がどうこうて観点で考えると、こうゆうのがよくない程度の話なのかな

package melon;

//不変なクラス
class Imutable {
    
    final private int a;
    protected Imutable(int a){
        this.a = a;
    }
    
    public static Imutable create(int a){
        return new Imutable(a);
    }
}

//不変でないクラス
class Child extends Imutable {
    public int b;

    protected Child(int a) {
        super(a);
    }
}

class Main{
    //imutable1は不変でない
    Imutable imutable1 = Child.create(100);
    //imutable2は不変である
    Imutable imutable2 = Imutable.create(100); 
}

この場合Imutableのインスタンスが不変であることを保証するためには、実装がImutableクラスのものであることをいちいち確認しないといけない。

まとめ

  • クラスはとにかく不変にする
  • 生成コストが高いクラスには不変クラス用のコンパニオンクラスを作る(Stringに対するStringBuilderみたいな)
  • 不変クラスを作るのが無理な場合も、出来る限り変更出来るポイントは減らす(状態の数を減らす)

コンストラクタは必要なやつ全部盛り込んだ奴が1つでそれ以外のパターンはstaticファクトリでいく感じがいいのかな?