1. 静的ネストクラス
静的ネストクラス(static nested class)とは、別のクラスの内部で修飾子 static を付けて宣言されたクラスです。本質的には、単に別のクラスの中に「住んでいる」だけの普通のクラスであり、外側クラスのオブジェクトとは結び付いていません。
内部クラスが「いつも(外側クラスの)お兄さんの手を握っている弟」のような存在だとすれば、静的ネストクラスは「親戚のいとこ」—家族行事にだけやって来て、それ以外の時間は自分の生活をしている—のような存在です。
主な違い:
- 外側クラスのオブジェクトへの暗黙の参照を持たない — OuterClass.this も非staticメンバーへのアクセスも不可。
- staticメンバーを持てる(通常の内部クラスとは異なる)。
- 外側クラスのオブジェクトなしで生成できる。
宣言の構文
静的ネストクラスの宣言は簡単です。外側クラスの内部でキーワード static を使います。
class Outer {
static class Nested {
void print() {
System.out.println("Hello from Nested!");
}
}
}
これで終わり。複雑な構文は不要 — ただの static class です。
可視化:
Outer(外側クラス)
│
├── Nested(静的ネストクラス)
│ └── print()
2. 静的ネストクラスのインスタンス生成
うれしいポイント: 外側クラスのオブジェクトは不要!
Outer.Nested nested = new Outer.Nested();
nested.print(); // Hello from Nested!
注目: クラスの完全名 — Outer.Nested — を使います。これは、入れ子のクラスに姓を付けて呼ぶようなものです。「Smith.Son」のように。
内部(inner)クラスと比較:
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner(); // outerオブジェクトが必要
一方、static nested では Outer のオブジェクトはまったく必要ありません!
3. 外側クラスのメンバーへのアクセス
ここが内部クラスとの最大の違いです。
- 静的ネストクラスがアクセスできるのは外側クラスのstaticメンバーのみです。
- 非staticのフィールドやメソッドにはアクセスできません(たとえ public でも)。
例:
class Outer {
private static int staticValue = 10;
private int instanceValue = 20;
static class Nested {
void show() {
System.out.println("Static value: " + staticValue); // OK
// System.out.println("Instance value: " + instanceValue); // エラー!
}
}
}
非staticフィールド instanceValue にアクセスしようとすると、コンパイラに怒られます。
なぜ? static nested class には、どの Outer のオブジェクトと結び付ければよいのかという情報がなく、外側クラスのオブジェクト参照を持っていないからです。
4. 静的ネストクラスを使う場面
どんなときに適切?
- ネストクラスが外側クラスと論理的に関連しているが、外側インスタンスへのアクセスは不要なとき。
- 補助的な構造をカプセル化したいとき(例: builder、ユーティリティ、enum、あるいは小さなイミュータブルオブジェクト)。
- パッケージの「散らかり」を減らしたいとき。クラスが外側クラス専用なら、外に出す必要はありません。
典型的なシナリオ:
- Builder パターン(特にイミュータブルオブジェクト)
- 補助的な構造の実装: たとえばコレクション内の内部 Node クラス
- 定数やユーティリティのグルーピング
簡単な判断ルール
自問してみてください: 「このネストクラスは外側クラスの特定のインスタンスへのアクセスが必要か?」
いいえ → static class(静的ネスト)を使う
はい → 通常の class(内部クラス)を使う
5. 使用例
例1: クラスのための Builder
クラス Person があり、パターン Builder を適用したいとします:
public class Person {
private final String name;
private final int age;
// 非公開コンストラクタ
private Person(Builder builder) {
this.name = builder.name;
this.age = builder.age;
}
// 静的ネストクラス Builder
public static class Builder {
private String name;
private int age;
public Builder setName(String name) {
this.name = name;
return this;
}
public Builder setAge(int age) {
this.age = age;
return this;
}
public Person build() {
return new Person(this);
}
}
public void printInfo() {
System.out.println("Person: " + name + ", " + age);
}
}
使い方:
Person person = new Person.Builder()
.setName("Ivan")
.setAge(30)
.build();
person.printInfo(); // Person: Ivan, 30
なぜ Builder は静的ネストクラスなのか?
それは Person のオブジェクトに依存せず、生成を助けるだけだからです。Person とは論理的に関連していますが、特定のインスタンスとは結び付いていません。
例2: コレクション内部の補助構造
「数値の箱」のようなクラスを考えます。要素の格納には内部の静的クラス Node を使います:
public class IntBox {
private Node head;
// ネストされた static class
private static class Node {
int value;
Node next;
Node(int value) {
this.value = value;
}
}
public void add(int value) {
Node node = new Node(value);
node.next = head;
head = node;
}
public void printAll() {
Node current = head;
while (current != null) {
System.out.println(current.value);
current = current.next;
}
}
}
使い方:
IntBox box = new IntBox();
box.add(1);
box.add(2);
box.add(3);
box.printAll(); // 3 2 1
なぜ Node は static なのか?
各 Node は箱(IntBox)全体を知る必要がなく、データと次の Node への参照だけを保持すればよいからです。
例3: メインの中のユーティリティクラス
public class MathUtils {
// 複素数を扱うための静的ネストクラス
public static class Complex {
private final double re;
private final double im;
public Complex(double re, double im) {
this.re = re;
this.im = im;
}
public Complex add(Complex other) {
return new Complex(this.re + other.re, this.im + other.im);
}
@Override
public String toString() {
return re + " + " + im + "i";
}
}
}
使い方:
MathUtils.Complex a = new MathUtils.Complex(1, 2);
MathUtils.Complex b = new MathUtils.Complex(3, 4);
MathUtils.Complex sum = a.add(b);
System.out.println(sum); // 4.0 + 6.0i
6. 有用なポイント
内部クラス vs 静的ネストクラス
| 内部(inner)クラス | 静的ネスト(static nested) | |
|---|---|---|
| キーワード | なし | |
| 外側オブジェクトへの暗黙参照 | はい | いいえ |
| 外側クラスの非staticメンバーへのアクセス | はい | いいえ |
| 外側クラスのstaticメンバーへのアクセス | はい | はい |
| staticメンバーを持てるか | いいえ(定数のみ) | はい |
| 生成の構文 | |
|
| 用途 | 外側クラスのオブジェクトへのアクセスが必要なとき | 外側クラスのオブジェクトへのアクセスが不要なとき |
図
flowchart LR
OuterClass -->|has| InnerClass
OuterClass -.->|has| StaticNestedClass
StaticNestedClass -.->|can access| staticMembers
InnerClass -->|can access| instanceMembers
InnerClass -->|can access| staticMembers
特徴と制約
- Static nested class は通常のフィールド/メソッドと static フィールド/メソッドの両方を含められます。
- 任意のアクセス修飾子(public、private、protected、package-private)で宣言できます。
- インターフェースを実装し、他のクラスを継承できます。
- generic にできます。
- 通常、外側クラスの外では不要な補助クラスをカプセル化するために使われます。
generic な static ネストクラスの例:
public class Box {
public static class Holder<T> {
private T value;
public Holder(T value) { this.value = value; }
public T get() { return value; }
}
}
static ネストクラスを使うべきでないとき
- ネストクラスが外側クラスの非staticフィールド/メソッドへのアクセスを必要とする場合 — 通常の inner class を使う。
- そのクラスが外側クラスの外でも必要な場合 — 別ファイルに切り出す。
- クラスが大き過ぎたり複雑な場合 — 独立させるほうが良い。
7. 典型的な間違いと注意点
誤り1: inner と static nested の取り違え。
多くの初心者は、static nested class から外側クラスの非staticメンバーにアクセスしようとします。しかし static nested class には外側クラスのオブジェクト参照がないため、これは不可能です。特定のオブジェクトの状態にアクセスしたいなら、通常の inner class を使いましょう。
誤り2: 外側クラスのオブジェクト経由で static nested class を生成しようとする。
outer.new Inner() のように書く必要はありません。static nested class には常に new Outer.Nested() を使います。
誤り3: 外側クラスのインスタンスへのアクセスを必要とするロジックに static nested class を使う。
クラスのロジックが外側クラスのオブジェクトの状態と密接に結び付いている場合、static nested class は不適切です。通常の内部クラスを使いましょう。
誤り4: 入れ子にし過ぎる。
ネストクラスの多用は避けましょう。構造が複雑になってきたら、一部のクラスは外に出したほうが良いです。
GO TO FULL VERSION