1. Statyczna klasa zagnieżdżona
Statyczna klasa zagnieżdżona (static nested class) — to klasa zadeklarowana wewnątrz innej klasy z modyfikatorem static. W istocie to zwykła klasa, która po prostu „żyje” wewnątrz innej klasy, ale nie jest powiązana z jej obiektem.
Jeśli klasa wewnętrzna jest jak młodszy brat, który zawsze trzyma się za rękę starszego (obiektu klasy zewnętrznej), to statyczna zagnieżdżona — to kuzyn, który przyjeżdża tylko na rodzinne święta, a przez resztę czasu żyje własnym życiem.
Kluczowe różnice:
- Nie ma niejawnego odwołania do obiektu klasy zewnętrznej — żadnego OuterClass.this i dostępu do niestatycznych składowych.
- Może zawierać składowe statyczne (w przeciwieństwie do zwykłej klasy wewnętrznej).
- Tworzy się ją bez obiektu klasy zewnętrznej.
Składnia deklaracji
Deklaracja statycznej klasy zagnieżdżonej jest bardzo prosta: używamy słowa kluczowego static wewnątrz klasy zewnętrznej.
class Outer {
static class Nested {
void print() {
System.out.println("Hello from Nested!");
}
}
}
I to wszystko! Żadnych wyrafinowanych konstrukcji składniowych — po prostu static class.
Wizualizacja:
Outer (klasa zewnętrzna)
│
├── Nested (static nested class)
│ └── print()
2. Tworzenie instancji statycznej klasy zagnieżdżonej
Co najważniejsze: nie jest potrzebny obiekt klasy zewnętrznej!
Outer.Nested nested = new Outer.Nested();
nested.print(); // Hello from Nested!
Zwróć uwagę: używamy pełnej nazwy klasy — Outer.Nested. To jak odwołanie się do klasy zagnieżdżonej przez nazwisko: „Smith.Son”.
Porównaj z klasą wewnętrzną (inner):
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner(); // potrzebny jest obiekt outer
A w przypadku static nested — obiekt Outer w ogóle nie jest potrzebny!
3. Dostęp do składowych klasy zewnętrznej
Tu kryje się główna różnica względem klasy wewnętrznej.
- Statyczna klasa zagnieżdżona ma dostęp tylko do składowych statycznych klasy zewnętrznej.
- Nie można odwoływać się do pól i metod niestatycznych (nawet jeśli są public).
Przykład:
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); // Błąd!
}
}
}
Jeśli spróbujesz odwołać się do niestatycznego pola instanceValue, kompilator natychmiast da ci nauczkę.
Dlaczego tak? Bo static nested class nie „wie”, z jakim obiektem Outer ma się powiązać — nie ma odwołania do obiektu klasy zewnętrznej.
Kiedy używać statycznych klas zagnieżdżonych
Kiedy są uzasadnione?
- Gdy klasa zagnieżdżona jest logicznie powiązana z klasą zewnętrzną, ale nie wymaga dostępu do jej obiektu.
- Gdy chcesz enkapsulować pomocniczą strukturę: np. builder, narzędzie, wyliczenie lub nawet mały obiekt niemutowalny.
- Gdy trzeba ograniczyć „zaśmiecanie” pakietu: klasa jest potrzebna tylko klasie zewnętrznej i nie ma sensu wynosić jej na zewnątrz.
Typowe scenariusze:
- Wzorzec Builder (zwłaszcza dla obiektów niezmiennych)
- Implementacja struktur pomocniczych: np. wewnętrzne klasy Node w kolekcjach
- Grupowanie stałych lub narzędzi
Prosta zasada wyboru
Zadaj sobie pytanie: „Czy mojej klasie zagnieżdżonej jest potrzebny dostęp do konkretnego obiektu klasy zewnętrznej?”
NIE → użyj static class (statycznej klasy zagnieżdżonej)
TAK → użyj zwykłej class (klasy wewnętrznej)
5. Przykłady użycia
Przykład 1: Builder dla klasy
Załóżmy, że mamy klasę Person i chcemy zaimplementować dla niej wzorzec Builder:
public class Person {
private final String name;
private final int age;
// Prywatny konstruktor
private Person(Builder builder) {
this.name = builder.name;
this.age = builder.age;
}
// Statyczna klasa zagnieżdżona 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);
}
}
Użycie:
Person person = new Person.Builder()
.setName("Iwan")
.setAge(30)
.build();
person.printInfo(); // Person: Iwan, 30
Dlaczego Builder to statyczna klasa zagnieżdżona?
Ponieważ nie zależy od obiektu Person, a jedynie pomaga go utworzyć. Jest logicznie powiązany z Person, ale nie z konkretną instancją.
Przykład 2: Struktura pomocnicza wewnątrz kolekcji
Wyobraźmy sobie klasę „pudełko na liczby”, gdzie do przechowywania elementów używana jest wewnętrzna statyczna klasa Node:
public class IntBox {
private Node head;
// Statyczna klasa zagnieżdżona
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;
}
}
}
Użycie:
IntBox box = new IntBox();
box.add(1);
box.add(2);
box.add(3);
box.printAll(); // 3 2 1
Dlaczego Node jest static?
Ponieważ każdy Node nie powinien znać całego pudełka (IntBox), tylko przechowuje dane i odwołanie do następnego Node.
Przykład 3: Klasa narzędziowa wewnątrz klasy głównej
public class MathUtils {
// Statyczna klasa zagnieżdżona do pracy z liczbami zespolonymi
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";
}
}
}
Użycie:
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. Przydatne niuanse
Klasa wewnętrzna vs statyczna klasa zagnieżdżona
| Wewnętrzna (inner) klasa | Statyczna zagnieżdżona (static nested) | |
|---|---|---|
| Słowo kluczowe | brak | |
| Niejawne odwołanie do obiektu zewnętrznego | tak | nie |
| Dostęp do niestatycznych składowych klasy zewnętrznej | tak | nie |
| Dostęp do statycznych składowych klasy zewnętrznej | tak | tak |
| Może zawierać składowe statyczne | nie (tylko stałe) | tak |
| Składnia tworzenia | |
|
| Zastosowanie | Gdy potrzebny jest dostęp do obiektu klasy zewnętrznej | Gdy dostęp do obiektu klasy zewnętrznej nie jest potrzebny |
Ilustracja
flowchart LR
OuterClass -->|has| InnerClass
OuterClass -.->|has| StaticNestedClass
StaticNestedClass -.->|can access| staticMembers
InnerClass -->|can access| instanceMembers
InnerClass -->|can access| staticMembers
Cechy i ograniczenia
- Static nested class może zawierać zarówno zwykłe, jak i statyczne pola oraz metody.
- Może być zadeklarowana z dowolnym modyfikatorem dostępu (public, private, protected, package-private).
- Może implementować interfejsy i dziedziczyć po innych klasach.
- Może być generic.
- Zwykle używana do enkapsulacji klas pomocniczych, które nie są potrzebne poza klasą zewnętrzną.
Przykład generycznej statycznej klasy zagnieżdżonej:
public class Box {
public static class Holder<T> {
private T value;
public Holder(T value) { this.value = value; }
public T get() { return value; }
}
}
Kiedy NIE warto używać statycznej klasy zagnieżdżonej
- Jeśli klasie zagnieżdżonej potrzebny jest dostęp do niestatycznych pól/metod klasy zewnętrznej — użyj zwykłej klasy wewnętrznej (inner class).
- Jeśli klasa jest potrzebna poza klasą zewnętrzną — wynieś ją do osobnego pliku.
- Jeśli klasa jest zbyt duża lub złożona — lepiej zrobić z niej osobną klasę.
7. Typowe błędy i niuanse
Błąd nr 1: Pomyłka między inner a static nested.
Wielu początkujących próbuje w static nested class odwoływać się do niestatycznych pól klasy zewnętrznej. Jednak static nested class nie ma odwołania do obiektu klasy zewnętrznej, więc to niemożliwe. Jeśli potrzebujesz dostępu do stanu konkretnego obiektu — użyj zwykłej klasy wewnętrznej (inner).
Błąd nr 2: Próba tworzenia static nested class poprzez obiekt klasy zewnętrznej.
Nie ma potrzeby pisać outer.new Inner(). Dla static nested class zawsze używaj new Outer.Nested().
Błąd nr 3: Używanie static nested class do logiki wymagającej dostępu do instancji klasy zewnętrznej.
Jeśli logika klasy jest ściśle związana ze stanem obiektu klasy zewnętrznej, static nested class to zły wybór. Użyj zwykłej klasy wewnętrznej.
Błąd nr 4: Zbyt skomplikowana zagnieżdżoność.
Nie nadużywaj klas zagnieżdżonych. Jeśli struktura staje się zagmatwana, lepiej wynieść część klas na zewnątrz.
GO TO FULL VERSION