class OuterClass {
...
static class StaticNestedClass {
...
}
class InnerClass {
...
}
}
Te wewnętrzne klasy są nazywane zagnieżdżonymi. Dzielą się na 2 rodzaje:
- Niestatyczne klasy zagnieżdżone. Są one również nazywane klasami wewnętrznymi.
- Statyczne klasy zagnieżdżone.
- lokalna klasa
- anonimowa klasa

public class Bicycle {
private String model;
private int weight;
public Bicycle(String model, int weight) {
this.model = model;
this.weight = weight;
}
public void start() {
System.out.println("Let's go!");
}
public class Handlebar {
public void right() {
System.out.println("Steer right!");
}
public void left() {
System.out.println("Steer left!");
}
}
public class Seat {
public void up() {
System.out.println("Seat up!");
}
public void down() {
System.out.println("Seat down!");
}
}
}
Tutaj mamy Bicycle
klasę. Ma 2 pola i 1 metodę: start()
. 
Handlebar
i Seat
. Ich kod jest napisany wewnątrz Bicycle
klasy. Są to pełnoprawne klasy: jak widać, każda z nich ma swoje własne metody. W tym momencie możesz mieć pytanie: dlaczego, u licha, mielibyśmy umieszczać jedną klasę w drugiej? Po co robić z nich klasy wewnętrzne? Załóżmy, że w naszym programie potrzebujemy osobnych klas dla pojęć kierownicy i siodełka. Oczywiście nie musimy ich zagnieżdżać! Możemy zrobić zwykłe zajęcia. Na przykład tak:
public class Handlebar {
public void right() {
System.out.println("Steer right!");
}
public void left() {
System.out.println("Steer left");
}
}
public class Seat {
public void up() {
System.out.println("Seat up!");
}
public void down() {
System.out.println("Seat down!");
}
}
Bardzo dobre pytanie! Oczywiście nie ogranicza nas technologia. Zrobienie tego jest z pewnością opcją. Tutaj ważniejsze jest bardziej poprawne zaprojektowanie zajęć z punktu widzenia konkretnego programu i jego przeznaczenia. Klasy wewnętrzne służą do oddzielenia jednostki, która jest nierozerwalnie połączona z inną jednostką. Kierownica, siodełko i pedały są elementami roweru. Oddzielone od roweru nie mają większego sensu. Gdybyśmy rozdzielili wszystkie te koncepcje na klasy publiczne, w naszym programie mielibyśmy taki kod:
public class Main {
public static void main(String[] args) {
Handlebar handlebar = new Handlebar();
handlebar.right();
}
}
Hmm... Znaczenie tego kodu jest nawet trudne do wyjaśnienia. Mamy jakąś niejasną kierownicę (Dlaczego to konieczne? Nie mam pojęcia, szczerze mówiąc). A ten uchwyt skręca w prawo... całkiem sam, bez roweru... z jakiegoś powodu. Oddzielając koncepcję kierownicy od koncepcji roweru, straciliśmy trochę logiki w naszym programie. Używając klasy wewnętrznej, kod wygląda zupełnie inaczej:
public class Main {
public static void main(String[] args) {
Bicycle peugeot = new Bicycle("Peugeot", 120);
Bicycle.Handlebar handlebar = peugeot.new Handlebar();
Bicycle.Seat seat = peugeot.new Seat();
seat.up();
peugeot.start();
handlebar.left();
handlebar.right();
}
}
Wyjście konsoli:
Seat up!
Let's go!
Steer left!
Steer right!
Teraz to, co widzimy, nagle nabiera sensu! :) Stworzyliśmy obiekt rowerowy. Stworzyliśmy dwa „podobiekty” rowerowe — kierownicę i siodełko. Podnieśliśmy siedzenie dla wygody i ruszyliśmy: pedałowanie i sterowanie w razie potrzeby! :) Metody, których potrzebujemy, są wywoływane na odpowiednich obiektach. To wszystko jest proste i wygodne. W tym przykładzie oddzielenie kierownicy i siodełka poprawia enkapsulację (ukrywamy dane o częściach roweru w odpowiedniej klasie) i pozwala nam stworzyć bardziej szczegółową abstrakcję. Spójrzmy teraz na inną sytuację. Załóżmy, że chcemy stworzyć program symulujący sklep rowerowy i części zamienne do rowerów. 
-
Obiekt klasy wewnętrznej nie może istnieć bez obiektu klasy zewnętrznej.
To ma sens: dlatego stworzyliśmy w naszym programie zajęcia
Seat
iHandlebar
— aby nie skończyć z osieroconymi kierownicami i siedzeniami.Ten kod się nie kompiluje:
public static void main(String[] args) { Handlebar handlebar = new Handlebar(); }
Wynika z tego jeszcze jedna ważna cecha:
-
Obiekt klasy wewnętrznej ma dostęp do zmiennych klasy zewnętrznej.
Na przykład dodajmy
int seatPostDiameter
do naszej klasy zmienną (reprezentującą średnicę sztycy)Bicycle
.Następnie w
Seat
klasie wewnętrznej możemy stworzyćdisplaySeatProperties()
metodę wyświetlającą właściwości siedzenia:public class Bicycle { private String model; private int weight; private int seatPostDiameter; public Bicycle(String model, int weight, int seatPostDiameter) { this.model = model; this.weight = weight; this.seatPostDiameter = seatPostDiameter; } public void start() { System.out.println("Let's go!"); } public class Seat { public void up() { System.out.println("Seat up!"); } public void down() { System.out.println("Seat down!"); } public void displaySeatProperties() { System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter); } } }
A teraz możemy wyświetlić te informacje w naszym programie:
public class Main { public static void main(String[] args) { Bicycle bicycle = new Bicycle("Peugeot", 120, 40); Bicycle.Seat seat = bicycle.new Seat(); seat.displaySeatProperties(); } }
Wyjście konsoli:
Seat properties: seatpost diameter = 40
Notatka:nowa zmienna jest deklarowana z najbardziej rygorystycznym modyfikatorem dostępu (
private
). A mimo to klasa wewnętrzna ma dostęp! -
Obiekt klasy wewnętrznej nie może zostać utworzony w metodzie statycznej klasy zewnętrznej.
Wyjaśnia to specyfika organizacji klas wewnętrznych. Klasa wewnętrzna może mieć konstruktory z parametrami lub tylko konstruktor domyślny. Ale niezależnie od tego, kiedy tworzymy obiekt klasy wewnętrznej, referencja do obiektu klasy zewnętrznej jest niewidocznie przekazywana do tworzonego obiektu klasy wewnętrznej. W końcu obecność takiego odniesienia do obiektu jest bezwzględnym wymogiem. W przeciwnym razie nie będziemy mogli tworzyć obiektów klasy wewnętrznej.
Ale jeśli metoda klasy zewnętrznej jest statyczna, możemy nie mieć obiektu klasy zewnętrznej! Byłoby to naruszeniem logiki działania klasy wewnętrznej. W tej sytuacji kompilator wygeneruje błąd:
public static Seat createSeat() { // Bicycle.this cannot be referenced from a static context return new Seat(); }
-
Klasa wewnętrzna nie może zawierać zmiennych statycznych i metod.
Logika jest taka sama: statyczne metody i zmienne mogą istnieć i być wywoływane lub przywoływane nawet w przypadku braku obiektu.
Ale bez obiektu klasy zewnętrznej nie będziemy mieli dostępu do klasy wewnętrznej.
Wyraźna sprzeczność! Dlatego statyczne zmienne i metody nie są dozwolone w klasach wewnętrznych.
Kompilator wygeneruje błąd, jeśli spróbujesz je utworzyć:
public class Bicycle { private int weight; public class Seat { // An inner class cannot have static declarations public static void displaySeatProperties() { System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter); } } }
-
Podczas tworzenia obiektu klasy wewnętrznej ważny jest jego modyfikator dostępu.
Klasę wewnętrzną można oznaczyć za pomocą standardowych modyfikatorów dostępu:
public
,private
,protected
, ipackage private
.Dlaczego to ma znaczenie?
Wpływa to na to, gdzie możemy tworzyć instancje klasy wewnętrznej w naszym programie.
Jeśli nasza
Seat
klasa jest zadeklarowana jakopublic
, możemy tworzyćSeat
obiekty w dowolnej innej klasie. Jedynym wymaganiem jest istnienie obiektu klasy zewnętrznej.Nawiasem mówiąc, zrobiliśmy to już tutaj:
public class Main { public static void main(String[] args) { Bicycle peugeot = new Bicycle("Peugeot", 120); Bicycle.Handlebar handlebar = peugeot.new Handlebar(); Bicycle.Seat seat = peugeot.new Seat(); seat.up(); peugeot.start(); handlebar.left(); handlebar.right(); } }
Łatwo uzyskaliśmy dostęp do
Handlebar
klasy wewnętrznej zMain
klasy.Jeśli zadeklarujemy klasę wewnętrzną jako
private
, będziemy mogli tworzyć obiekty tylko wewnątrz klasy zewnętrznej.Nie możemy już tworzyć
Seat
obiektu „na zewnątrz”:private class Seat { // Methods } public class Main { public static void main(String[] args) { Bicycle bicycle = new Bicycle("Peugeot", 120, 40); // Bicycle.Seat has private access in Bicycle Bicycle.Seat seat = bicycle.new Seat(); } }
Pewnie już rozumiesz logikę :)
-
Modyfikatory dostępu dla klas wewnętrznych działają tak samo jak dla zwykłych zmiennych.
Modyfikator
protected
zapewnia dostęp do zmiennej instancji w podklasach i klasach, które znajdują się w tym samym pakiecie.protected
działa również dla klas wewnętrznych. Możemy tworzyćprotected
obiekty klasy wewnętrznej:- w klasie zewnętrznej;
- w swoich podklasach;
- w klasach, które są w tym samym pakiecie.
Jeśli klasa wewnętrzna nie ma modyfikatora dostępu (
package private
), można utworzyć obiekty klasy wewnętrznej:- w klasie zewnętrznej;
- w klasach, które są w tym samym pakiecie.
Od dawna znasz modyfikatory, więc tutaj nie ma problemów.
GO TO FULL VERSION