javaでは、抽象メソッドを持つクラスは必ず抽象クラス(かインタフェース)になると決まっている。
しかし抽象内部クラスを持つクラスは、別に具体的クラスでも構わないのである。
例えば次のようなコードは実行可能である。
final class A
{
static abstract class B{String str="hoge";}
}
public class Main
{
public static void main(String...args)
{
System.out.println(new A_B().str);
//System.out.println(new A.B(){}.str); (匿名クラス)でも別に良い
}
}
class A_B extends A.B{}
もし具体クラスが抽象内部クラスを持てなかったら
一見矛盾する仮定ではありますが、「マクロ(全体)に多様性を認めたくないが、ミクロ(細部)に多様性を認めたい」という場合を考えます。
このような仮定は統計力学など、ミクロとマクロの関連を考えるとき役立ちます。
例えばAir
(空気)クラスとMolecule
(分子)クラスを作ったとします。
分子が集まって空気を作りますので、Air
has a Molecule[]
といえるでしょう。
分子には水素分子、水分子、二酸化炭素分子等いろいろ考えられます。
したがって、
H2 extends Molecule
, H2O extends Molecule
, CO2 extends Molecule
のように継承して使う一方、「単なる分子」という分子は存在しないので、継承せずに使うことはないと思います。
そこでMolecule
は抽象クラスとなりそうです。
一方、Air
には下位概念が考えにくいので、抽象クラスにしたくはありません。
ここでもし、Molecule
にAir
からしか呼ばれたくないメソッドを定義したくなったらどうしましょう。
いつもならMolecule
をAir
のインナークラスにすることで解決できます。
もし具体クラスAir
が、抽象クラスMolecule
を内部に持てなかったら、多分次のような面倒くさいことをしなくちゃいけない。
-
final public static class ${private $(){}}
をAir
に持たせる -
Something
のメソッドにAir.$
型引数を持たせる
で実現する。
Air.$
型はpublic static
でAir
以外からも見えるため、Air
以外のクラスのメソッドの仮引数型として利用できる。
しかしそのコンストラクタはprivate
なので、インスタンス生成はAir
からしか行えない。Air
以外はすなわちAir.$
型実引数を渡せない。
そのおかげで、「仮引数にAir.$
型を持つメソッドならば、そのメソッドはAir
クラスからしか呼べない」が成立する。
実例を以下に示す。
public class Test2 {
public static void main(String[] args)
{
//new H2O.only_for_air(new Air.$()); はアクセス権限のエラー。つまりAir以外からonly_for_airを呼べない
new Air().molecule_caller(new H2O());//Airからは呼べる
}
}
class Air
{
final public static class ${private $(){}}//Airの子クラスで書き換えされても困るのでfinalizeした
void molecule_caller(Molecule m)
{
new H2O().only_for_air(new Air.$());
}
}
abstract class Molecule
{
void only_for_air(Air.$ $)
{
System.out.println("succeeded");
}
}
class H2O extends Molecule{}
とまあ、こんな面倒なことをしなくちゃいけなくなる。
だから「抽象内部クラスを持つ具体的クラス」がjavaでは可能なんだろうなと思った次第。