お前のそれはインナークラスじゃない
この記事は 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」より)。
- 明示的、あるいは、暗黙的に
static
ではないメンバークラス - 暗黙的に
static
ではないローカルクラス - 匿名クラス
一応、それぞれの例を書いておこう。
- 明示的、あるいは、暗黙的に
static
ではないメンバークラス
public class メイン1 {
public static void main(String[] args) {
new メイン1().new インナー1().叫ぶ();
}
public class インナー1 { // <- インナークラス
public void 叫ぶ() {
System.out.println("わいはインナークラスや!");
}
}
}
- 暗黙的に
static
ではないローカルクラス
public class メイン2 {
public static void main(String[] args) {
class インナー2 { // <- インナークラス
public void 叫ぶ() {
System.out.println("わいはインナークラスや!");
}
}
new インナー2().叫ぶ();
}
}
- 匿名クラス
interface 叫べる {
void 叫ぶ();
}
public class メイン3 {
public static void main(String[] args) {
new 叫べる() { // <- インナークラス
@Override
public void 叫ぶ() {
System.out.println("わいはインナークラスや!");
}
}.叫ぶ();
}
}
ここで、「暗黙的に static
ではない」と言う言い方をしているのは、以下のものは暗黙的に、つまり、ソースコード上に static
と言うキーワードが出てきていなくても、static
だからである。
- メンバークラスのケース
- インタフェースのメンバクラス
- メンバ
enum
クラス - メンバ
record
クラス(Java 16~)
- ローカルクラスのケース
- ローカル
enum
クラス(Java 16~) - ローカル
record
クラス(Java 16~)
- ローカル
なぜそのグルーピングなのか?
正直言って、「ネストしたクラス」のうち、なぜこれらの範囲のモノだけを「インナークラス」と言う名前でグルーピングしているのかは、イマイチ理解していない。
特に不思議なのは、上記のインナークラスの定義のうち、2 と 3 には「エンクロージングインスタンスへの暗黙の参照を保持していない」モノがあることである。
ちなみに「エンクロージングインスタンス」と言うのは、そのクラスを囲むクラス(エンクロージングクラス)のインスタンス、である。
例えば、上記の例ではインナー1
はエンクロージングインスタンスへの参照を保持しているが(だから new
の書き方がちょっとキモい)、インナー2
とインナー3
はエンクロージングインスタンスへの参照は保持していない。これは、インナー2
とインナー3
が static
メソッド内で宣言されているからである(このような箇所を static
コンテキストと言う)。
この「static
コンテキスト」で宣言したローカルクラスや匿名クラスを「インナークラス」に分類した理由をご存じの方はコメントででもお教え頂けると幸いである。
Discussion