インナークラス、匿名クラス
(2008/10/22に加筆。)
Javaではインナークラス、匿名クラスの違いがわかってると便利です。
以下のクラスから参照できる変数スコープの違いをはっきりと示すことができるとかっこいいでしょう。
- staticなインナークラス
- ただのインナークラス
- 匿名クラス
- メソッド内クラス
- メソッド内匿名クラス
メソッド内の匿名クラスは便利です。ただ、メソッドのローカル変数にはfinal変数以外にアクセスできなくなってしまうことを押さえておく必要があります。
とにかくメソッドローカル変数はfinalにしかアクセスできないので、それら変数を参照・更新・代入するにはfinalな変数に束縛されたmutableなオブジェクトか、finalに束縛された配列を用いる必要があります。
final MutableInt i = new MutableInt(0); // Apache Commons Langでさがしてみよう! int j = 0; final int[] k = {0}; new Lambda(){ void doIt(){ i.increment(); // 合法 j++; // 違法 k[0]++; // 合法 } }.doIt();
前述したとおり、finalな配列の要素にアクセスするように匿名クラス内部でコードをかけば代入や更新などし放題ですが、それだけのために配列を作成するのは不吉なので*1出来るならばMutableなオブジェクトを用意しましょう。
プリミティブ型に対するMutableオブジェクトはApache Commons Langにあるので、それとは別に任意のオブジェクトに関してMutablityを提供するクラスを用意しておくと匿名クラスを使いこなす上では便利でしょう:
public class MutableObject<T> { private T value; public MutableObject() { } public MutableObject(final T initialValue) { this.value = initialValue; } public void set(final T newValue) { this.value = newValue; } public T get() { return this.value; } @Override public int hashCode() { return this.value == null ? 0 : value.hashCode(); } @Override public boolean equals(final Object obj) { // 入れ物と中身をわけ隔てない寛容な#equals. 実際にはMutable同士の場合だけvalueを比較するだけのほうが安全でしょう。 if (obj == null) { return this.value == null; } if (obj instanceof MutableObject) { final Object objValue = ((MutableObject<?>) obj).value; return objValue == null ? this.value == null : // objValue.equals(objValue); } else { return this.value != null && this.value.equals(obj); } } @Override public String toString() { return String.valueOf(this.value); } }
2010/05/07 追記: 上記コードのequalsはequalsの契約のうち、a.equals(b) <=> b.equals(a) を破っているのでよろしくありません。