Klasa zagnieżdżona to klasa zdefiniowana wewnątrz innej klasy. Java ma cztery rodzaje — statyczne zagnieżdżone, wewnętrzne (niestałe), lokalne i anonimowe — każdy z różnym dostępem do klasy otaczającej i różnymi przypadkami użycia.
1. Statyczna klasa zagnieżdżona — niezależna od instancji
public class Outer {
private static int data = 10;
static class Nested { // static → no reference to an Outer INSTANCE
void show() {
System.out.println(data); // can access static members of Outer only
}
}
}
Outer.Nested n = new Outer.Nested(); // created without an Outer instance
Statyczna klasa zagnieżdżona zachowuje się jak klasa na poziomie najwyższym, ale jest zawarta w innej klasie. Nie przechowuje referencji do instancji zewnętrznej — używana do grupowania klas pomocniczych (np. Builder, Node w strukturze danych).
2. Klasa wewnętrzna (niestałą) — związana z instancją
public class Outer {
private int value = 5;
class Inner { // non-static → has an implicit reference to Outer
void show() {
System.out.println(value); // can access Outer's INSTANCE members (even private)
}
}
}
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner(); // needs an Outer instance to create
Niestała klasa wewnętrzna przechowuje niejawną referencję do swojej instancji otaczającej, więc może uzyskać dostęp do pól instancji obiektu zewnętrznego. (Uwaga: ta referencja może spowodować wycieki pamięci, jeśli instancja wewnętrzna przeżyje instancję zewnętrzną.)
3. Klasa lokalna — zdefiniowana wewnątrz metody
void process() {
class Helper { // local to this method
void run() { ... }
}
new Helper().run();
}
Rzadko używana; ograniczona do zakresu metody.
4. Klasa anonimowa — jednorazowa implementacja bez nazwy
// implement an interface/abstract class inline, for single use
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) { handleClick(); }
});
Runnable task = new Runnable() {
public void run() { System.out.println("running"); }
};
Klasa anonimowa to jednorazowa implementacja interfejsu lub klasy, zdefiniowana i utworzona jednocześnie — historycznie powszechna dla programów obsługi zdarzeń i callback'ów. Nowoczesna Java często zastępuje je lambdami, gdy cel jest interfejsem funkcyjnym:
button.addActionListener(e -> handleClick()); // ✅ lambda — much cleaner
Runnable task = () -> System.out.println("running");
Dlaczego to ważne
Klasy zagnieżdżone to przydatne narzędzie do logicznego grupowania klas, które są używane tylko w jednym kontekście, zwiększające enkapsulację i czytelność.
Zrozumienie rodzajów — szczególnie rozróżnienia między statycznymi a wewnętrznymi (statyczne = niezależne, wewnętrzne = przechowują referencję do instancji otaczającej) — jest ważne zarówno dla projektowania, jak i unikania błędów: niejawna referencja zewnętrzna klas wewnętrznych może spowodować wycieki pamięci, a wybór statycznej, gdy nie potrzebujesz instancji zewnętrznej, jest czystszą opcją domyślną.
Klasy anonimowe były klasycznym sposobem na zapewnienie implementacji wbudowanych (programy obsługi zdarzeń, komparatory), a wiedza, że są w dużej mierze zastępowane przez lambdy dla interfejsów funkcyjnych, odzwierciedla nowoczesną biegłość w Javie.
Pojawiają się one w całej bibliotece standardowej (np. Map.Entry to zagnieżdżony interfejs, budowniczowie to statyczne klasy zagnieżdżone), co czyni je praktyczną, często spotykaną wiedzą.
