effective java の 防御的コピーの話を自分なりに実装してみた

エキサイト株式会社の中尾です。

effective java を最近読んでいて、気になった部分を自分でコーディングして理解を深めようと思っています。 その一部を紹介します。

public class Main {

    public static void main(String[] args) {

        System.out.println("元のクラス");
        Counter counter = new Counter(100);

        System.out.println("変更可能なクラス");
        MutableCounter mutableCounter = new MutableCounter(counter);
        System.out.println("初期値 : " + mutableCounter.getCounter().get());

        counter.set(10);
        System.out.println("10で更新される : " + mutableCounter.getCounter().get());
        System.out.println("理由はcounterとmutableCounterが等しいため");
        System.out.println(counter.hashCode() + " = " + mutableCounter.getCounter().hashCode());
        System.out.println("trueになる : " + counter.equals(mutableCounter.getCounter()));

        System.out.println();
        System.out.println("不完全な不変クラス");
        counter = new Counter(100);
        BlockCounter blockCounter = new BlockCounter(counter);

        counter.set(10);
        System.out.println("10で更新されない : " + blockCounter.getCounter().get());
        System.out.println("理由はcounterとblockCounterが等しくないため");
        System.out.println(counter.hashCode() + " = " + blockCounter.getCounter().hashCode());
        System.out.println("falseになる : " + counter.equals(blockCounter.getCounter()));
        blockCounter.getCounter().set(20);
        System.out.println("blockCounter.getCounter() から counterを参照できるため、更新することができる : " + blockCounter.getCounter().get());
        System.out.println("当たり前だけど一致する");
        System.out.println(blockCounter.getCounter().hashCode() + " = " + blockCounter.getCounter().hashCode());
        System.out.println("trueになる : " + blockCounter.getCounter().equals(blockCounter.getCounter()));
        System.out.println();

        System.out.println("完全な不変クラス");
        counter = new Counter(100);
        ImmutableCounter immutableCounter = new ImmutableCounter(counter);

        System.out.println(immutableCounter.getCounter().get());
        counter.set(10);
        System.out.println("10で更新できない : " + immutableCounter.getCounter().get());
        immutableCounter.getCounter().set(20);
        System.out.println("20で更新できない : " + immutableCounter.getCounter().get());

        System.out.println("immutableCounter.get()するたびに新しいオブジェクトが作成されるため、ハッシュコードが毎回変わる");
        System.out.println(immutableCounter.getCounter().hashCode()
                + " != " + immutableCounter.getCounter().hashCode()
                + " != " + immutableCounter.getCounter().hashCode());
    }
}

setterとgetterのみを持つクラスです。 数値を設定できます。

public class Counter {
    private int count;

    public Counter(int count){
        this.count = count;
    }

    public int get(){
        return this.count;
    }

    public void set(int i){
        this.count = i;
    }
}

上記のカウンタークラスを使うMutableCounterクラスです。 変更可能なクラスです。

public class MutableCounter {

    private final Counter counter;

    public MutableCounter(Counter sampleCounter){
        this.counter = sampleCounter;
    }

    public Counter getCounter(){
        return this.counter;
    }
}

上記のカウンタークラスを使うBlockCounterクラスです。 少しだけ防御力があがっています。

public class BlockCounter {
    private final Counter counter;

    public BlockCounter(Counter sampleCounter){
        this.counter = new Counter(sampleCounter.get());
    }

    public Counter getCounter(){
        return this.counter;
    }
}

上記のカウンタークラスを使う ImmutableCounterクラスです。 完全に防御されているクラスです。

public class ImmutableCounter {
    private final Counter counter;

    public ImmutableCounter(Counter sampleCounter){
        this.counter = new Counter(sampleCounter.get());
    }

    public Counter getCounter(){
        return new Counter(this.counter.get());
    }
}

実行結果です。

元のクラス
変更可能なクラス
初期値 : 100
10で更新される : 10
理由はcounterとmutableCounterが等しいため
951007336 = 951007336
trueになる : true

不完全な不変クラス
10で更新されない : 100
理由はcounterとblockCounterが等しくないため
834600351 = 471910020
falseになる : false
blockCounter.getCounter() から counterを参照できるため、更新することができる : 20
当たり前だけど一致する
471910020 = 471910020
trueになる : true

完全な不変クラス
100
10で更新できない : 100
20で更新できない : 100
immutableCounter.get()するたびに新しいオブジェクトが作成されるため、ハッシュコードが毎回変わる
1418481495 != 303563356 != 135721597

effective javaに書いている通り、不変クラスが作ることができました。 不変クラスを作れるとかっこいいですね!!! かっこいいけど、柔軟性も欲しい気がするのが私の気持ちです。