🐔

お前のそれはインナークラスじゃない

2021/12/01に公開

この記事は Java Advent Calendar 2021 の第 1 日目の記事である。

なお、予め断っておくが、この記事を読んでも得られるものは何も無い。

インナークラス?

どうも世の中にはネストしたクラスは何でもインナークラス(内部クラス)だと思っている人たちが一定数いるようである。
しかし、ネストしたクラスのうち static なメンバークラスはインナークラスとは呼ばないので注意されたい。

public class メイン {
    public static void main(String[] args) {
        new ネスト().叫ぶ();
    }
    public static class ネスト {                 // <- インナークラスではない
        public void 叫ぶ() {
	    System.out.println("インナークラスと呼ばないで!");
	}
    }
}

個人のブログ等で混同している分にはまぁいいが(あんまり良くはないが)、あろうことか大手(?)技術解説サイトなどでも平気でウソを書いているのはいかがなものかと思う。(まぁ適当さに定評があるこことかこことかだが)

じゃあこれは何と呼ぶのかと言うと、インナークラスもひっくるめて、「ネストしたクラス」、「ネストクラス」、「ネステッドクラス」と呼ぶ事が多い気がするが、これが一番定着していると言う用語は無いように思える。
もし「これだ」と言う決定打をご存じの方はコメントででもお教え頂けると幸いである。

インナークラスの正確な定義

さて、「インナークラス」と言うのは下記の 3 種類の総称である(Java 言語仕様 8.1.3「Inner Classes and Enclosing Instances」より)。

  1. 明示的、あるいは、暗黙的に static ではないメンバークラス
  2. 暗黙的に static ではないローカルクラス
  3. 匿名クラス

一応、それぞれの例を書いておこう。

  1. 明示的、あるいは、暗黙的に static ではないメンバークラス
public class メイン1 {
    public static void main(String[] args) {
        new メイン1().new インナー1().叫ぶ();
    }
    public class インナー1 {                 // <- インナークラス
        public void 叫ぶ() {
	    System.out.println("わいはインナークラスや!");
	}
    }
}
  1. 暗黙的に static ではないローカルクラス
public class メイン2 {
    public static void main(String[] args) {
        class インナー2 {                 // <- インナークラス
            public void 叫ぶ() {
	        System.out.println("わいはインナークラスや!");
	    }
        }
        new インナー2().叫ぶ();
    }
}
  1. 匿名クラス
interface 叫べる {
    void 叫ぶ();
}

public class メイン3 {
    public static void main(String[] args) {
        new 叫べる() {                 // <- インナークラス
            @Override
            public void 叫ぶ() {
                System.out.println("わいはインナークラスや!");
            }
        }.叫ぶ();
    }
}

ここで、「暗黙的に static ではない」と言う言い方をしているのは、以下のものは暗黙的に、つまり、ソースコード上に static と言うキーワードが出てきていなくてもstatic だからである。

  1. メンバークラスのケース
    1. インタフェースのメンバクラス
    2. メンバ enum クラス
    3. メンバ record クラス(Java 16~)
  2. ローカルクラスのケース
    1. ローカル enum クラス(Java 16~)
    2. ローカル record クラス(Java 16~)

なぜそのグルーピングなのか?

正直言って、「ネストしたクラス」のうち、なぜこれらの範囲のモノだけを「インナークラス」と言う名前でグルーピングしているのかは、イマイチ理解していない。

特に不思議なのは、上記のインナークラスの定義のうち、2 と 3 には「エンクロージングインスタンスへの暗黙の参照を保持していない」モノがあることである。

ちなみに「エンクロージングインスタンス」と言うのは、そのクラスを囲むクラス(エンクロージングクラス)のインスタンス、である。

例えば、上記の例ではインナー1はエンクロージングインスタンスへの参照を保持しているが(だから new の書き方がちょっとキモい)、インナー2インナー3はエンクロージングインスタンスへの参照は保持していない。これは、インナー2インナー3static メソッド内で宣言されているからである(このような箇所を static コンテキストと言う)。

この「static コンテキスト」で宣言したローカルクラスや匿名クラスを「インナークラス」に分類した理由をご存じの方はコメントででもお教え頂けると幸いである。

Discussion