ちょっとtwitterで話題になったこと。

Javaのfinalというのは、その変数の参照先が変わらない(参照先が別のオブジェクトに切り替わらない)というだけで、参照先に存在するオブジェクト自体のステート不変は保証されていない。

final FooBean bar = new FooBean();
bar.setBuz("hoge");	// エラーは起こらない
/* bar = new FooBean(); */	// エラー

しかし、ステートが不変である事も明示的に保証したい場合がある。

参照するオブジェクトの中身も不変であることを保証するsuper-finalも欲しいと思った時期もあったが、仕様がむずかしそーw と思って主張を諦めた。

都元ダイスケ🍅 on Twitter: "ついでに、参照するオブジェクトの中身も不変であることを保証するsuper-finalも欲しいと思った時期もあったが、仕様がむずかしそーw と思って主張を諦めた。"

@daisuke_m super-final って、クラス修飾子に、 immutable とかがあったりすると目的が解決するんかな。まぁ、 super-final とは適用範囲が多少違うんだけど。

やましろ on Twitter: "@daisuke_m super-final って、クラス修飾子に、 immutable とかがあったりすると目的が解決するんかな。まぁ、 super-final とは適用範囲が多少違うんだけど。"

@yamashiro んーー、クラスに適用するものじゃなくて。もっと欲を言えば「宣言した後散々いじったけど、今後こいつは不変です」というような、freezeメソッドが欲しかったり?w

都元ダイスケ🍅 on Twitter: "@yamashiro んーー、クラスに適用するものじゃなくて。もっと欲を言えば「宣言した後散々いじったけど、今後こいつは不変です」というような、freezeメソッドが欲しかったり?w"

.@daisuke_m オブジェクトの状態を固定化したいときは、 Lockable Object Pattern( オレオレパターン ) による lock/unlock メソッドで実現するとか。< super-final *Tw*

Yasuharu Nakano on Twitter: "@daisuke_m オブジェクトの状態を固定化したいときは、 Lockable Object Pattern( オレオレパターン ) による lock/unlock メソッドで実現するとか。< super-final *Tw*"

@daisuke_m stationary fieldですね。みんな同じこと考えてるw http://tinyurl.com/238wd5

Takuto Wada on Twitter: "@daisuke_m stationary fieldですね。みんな同じこと考えてるw http://tinyurl.com/238wd5"

@yamashiro もっと欲を言えば、defreezeも欲しいな。凍結解除。例えば、cloneした後に解除すれば、片方だけ解除されるとか。

都元ダイスケ🍅 on Twitter: "@yamashiro もっと欲を言えば、defreezeも欲しいな。凍結解除。例えば、cloneした後に解除すれば、片方だけ解除されるとか。"

@daisuke_m オブジェクトがどういうやりとりされるときにそれが必要になるのかが想像がつかないなー。今度会ったときに図解頼むw

やましろ on Twitter: "@daisuke_m オブジェクトがどういうやりとりされるときにそれが必要になるのかが想像がつかないなー。今度会ったときに図解頼むw"

@daisuke_m 多分一緒ですね。サブシステムをまたがるときとかある種の責任境界を越えるときには、 Immutable にしてから渡したい、みたいなときに使います。 *Tw*

Yasuharu Nakano on Twitter: "@daisuke_m 多分一緒ですね。サブシステムをまたがるときとかある種の責任境界を越えるときには、 Immutable にしてから渡したい、みたいなときに使います。 *Tw*"

@yamasiro 同意。@daisuke_m 図解頼む

t_ishida on Twitter: "@yamasiro 同意。@daisuke_m 図解頼む"

@daisuke_m 図解に大賛成なのでよろしくお願いします。

いわまりょう on Twitter: "@daisuke_m 図解に大賛成なのでよろしくお願いします。"

というわけで、図解というよりもコードで語ってみた。

public class Main {
    
    public static void main(String[] args) {
        new Main().run();
    }
    
    private void run() {
        FooBean bar = new FooBean(); // mutable
        bar.setBuz("hoge"); // 色々mutation
        bar.freeze(); // method内ではimmutableで居て欲しい。
        method(bar); // いってらっしゃい
        System.out.println(bar.getBuz()); // hogeのまま変わって居ない事を期待
        bar.defreeze(); // 引き続きmutationしたい
        bar.setBuz("fuga"); // mutation可能
    }
    
    private void method(FooBean foo) {
        // 各種処理。
        // このメソッド内で、fooはimmutable。読み込みのみが行われる。
        /* foo.setBuz("piyo"); *// / これを実行するとRuntimeException
        foo.getBuz(); // getはOK
    }
}

public class FooBean {
    private String buz;
    private boolean freezed = false;
    
    public String getBuz() {
        return buz;
    }

    public void setBuz(String buz) {
        checkFreezed();
        this.buz = buz;
    }


    private void checkFreezed() {
        if(freezed) {
            throw new RuntimeException("freezed");
        }
    }

    public void freeze() {
        freezed = true;
    }
    
    public void defreeze() {
        freezed = false;
    }
}

@daisuke_m この例でいえば、 method が他のクラスのオブジェクトだとすると比較的 defreeze の必要性が理解できるかも新米。

やましろ on Twitter: "@daisuke_m この例でいえば、 method が他のクラスのオブジェクトだとすると比較的 defreeze の必要性が理解できるかも新米。"

@yamashiro そうね。動くコード化する為に、メソッド使ってるけど、外部オブジェクト(しかも自分の制御外)の時の方が必要性が高い。

都元ダイスケ🍅 on Twitter: "@yamashiro そうね。動くコード化する為に、メソッド使ってるけど、外部オブジェクト(しかも自分の制御外)の時の方が必要性が高い。"

@nobeans というか、コレをコードでカバーするんじゃなくて言語仕様でカバーしてくんねぇかなぁ、という無茶な望みw

都元ダイスケ🍅 on Twitter: "@nobeans というか、コレをコードでカバーするんじゃなくて言語仕様でカバーしてくんねぇかなぁ、という無茶な望みw"

Javaに勝手にオレオレ言語仕様を追加してみるw freezable修飾子と、freeze/defreezeステートメントを追加w

public class Main {
    
    public static void main(String[] args) {
        new Main().run();
    }
    
    private void run() {
        freezable FooBean bar = new FooBean();
        bar.setBuz("hoge");
        freeze bar;
        method(bar);
        System.out.println(bar.getBuz());
        defreeze bar;
        bar.setBuz("fuga");
    }
    
    private void method(FooBean foo) {
        // 各種処理。
        // このメソッド内で、fooはimmutable。読み込みのみが行われる。
        /* foo.setBuz("piyo"); */// これを実行するとRuntimeException
        foo.getBuz(); // getはOK
    }
}

public class FooBean {
    private String buz;
    
    public String getBuz() {
        return buz;
    }

    public void setBuz(String buz) {
        this.buz = buz;
    }
}