今日読んだところ
67 ページ〜 85 ページ
パッケージプライベート
同じ jar の同じパッケージからのみ参照できる
protected, public
完全公開 API として保守していかなければならないよ。
private とパッケージプライベートとは全然違うレベルよ
インスタンスフィールド
private にしる!
public static final で、その参照先が不変クラスな場合に限り private じゃなくてもいい
public static final で配列とか持たないようにね
ミューテーター
オブジェクトの状態を変更するようなメソッドをミューテーターっていうんだね。
不変クラス 5 条件
- ミューテーターを持たない
- クラスを final にする
- すべてのフィールドを final にする
- すべてのフィールドを private にする
- 他のオブジェクトと可変オブジェクトを共有しない。(防御的コピーを使って)
不変クラスの利点
- 作成したときの状態から変化することがないので。誰とその変数を共有しているか考えなくていい
- たとえば、「この関数に渡したら、この関数の中で値が変わらないかなー」とか考えなくてもいい
- がんがん共有できて気持ちいいい
- 本質的にスレッドセーフ
- 値が変わらないので、同期をとる必要がない。
- ハッシュが生成時に決まる
不変クラスの生成
不変クラスは private コンストラクタ, static ファクトリーメソッドと相性がいい。
生成時に値を決めなきゃいけないので、 static ファクトリーメソッドでいろいろなファクトリーを作るといい
可変クラス作るな!
よっぽど正当な理由がないかぎり、不変クラスにしる!
セッターを作りたい衝動は、禁止です><
転送クラス
あるインタフェースにおける Proxy クラスを作るためのスーパークラス。
転送クラスを使えば、簡単に Proxy クラスが作れる。
SELF 問題
Proxy パターンでは、 Proxy 経由で Real なクラスのメソッドが呼ばれることが想定されているが。
Real なクラスが自分の参照を含むオブジェクトを返す場合、そのオブジェクトは Real なクラスのメソッドを直接呼び出すことになる。
これを SELF 問題という
SELF 問題の例
import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; // インターフェースがあって interface AInterface { public ActionListener createListener(); public void say(); } // インターフェースを継承した実装 class A implements AInterface { // アクションリスナーを返す // このとき、アクションリスナーは this を参照している @Override public ActionListener createListener() { return new Listener(this); } // インスタンスメソッドの実装 @Override public void say() { System.out.println("I'm A."); } } // クラス A の Proxy class AProxy implements AInterface { private AInterface impl; public AProxy(A a) { impl = a; } @Override public ActionListener createListener() { return this.impl.createListener(); } // say は Proxy で拡張する @Override public void say() { impl.say(); System.out.println("But, I'm Proxy."); } } // アクションリスナーの実装 class Listener implements ActionListener { private AInterface parent; public Listener(AInterface parent) { this.parent = parent; } @Override public void actionPerformed(ActionEvent e) { parent.say(); } } public class Main { public static void main(String[] args) { JFrame frame = new JFrame(); JButton button = new JButton("Click me"); frame.getContentPane().add(button); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setVisible(true); AInterface a = new AProxy(new A()); // ここでボタンにアクションリスナーを登録 button.addActionListener(a.createListener()); } }
このような場合にボタンがクリックされたときに AProxy の say は呼ばれず。 A の say が呼ばれてしまう。
これを SELF 問題という。
それでも継承するときは
すべての B が A であるという関係が成り立つことを念入りに確認すべし。
(A がスーパークラス、 B がサブクラス)
まとめ
週末も勉強するぞー!