Klasy wewnętrzne, zagnieżdżone i anonimowe
Część sekcji Programowanie obiektowe ścieżki Java w Coddy. Lekcja 49 z 87.
W Javie możesz definiować klasy wewnątrz innych klas. Te klasy wewnętrzne pomagają organizować kod, grupując powiązane funkcjonalności, i mogą bezpośrednio uzyskiwać dostęp do składowych klasy zewnętrznej.
Zagnieżdżona klasa wewnętrzna jest zdefiniowana jako składowa innej klasy. Ma dostęp do wszystkich pól i metod klasy zewnętrznej, w tym prywatnych:
class Outer {
private String message = "Hello";
class Inner {
void display() {
System.out.println(message); // Ma dostęp do pola prywatnego
}
}
}
// Użycie
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.display(); // Wypisuje: HelloLokalna klasa wewnętrzna jest definiowana wewnątrz metody. Można jej używać tylko w tej metodzie i ma dostęp do zmiennych lokalnych, które są efektywnie finalne:
void process() {
String prefix = "Result: ";
class LocalHelper {
void print(int value) {
System.out.println(prefix + value);
}
}
new LocalHelper().print(42);
}Anonimowa klasa wewnętrzna to klasa bez nazwy, tworzona i instancjonowana w jednym wyrażeniu. Jest powszechnie używana do implementowania interfejsów lub rozszerzania klas na bieżąco:
Comparator<String> comp = new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
};Klasy wewnętrzne są szczególnie przydatne, gdy klasa ma znaczenie tylko dla innej klasy, dzięki czemu kod pozostaje uporządkowany i hermetyczny.
Wyzwanie
ŁatwyZbudujmy system koszyka zakupowego, który pokazuje wszystkie trzy typy klas wewnętrznych. Utworzysz ShoppingCart, który używa zagnieżdżonej klasy wewnętrznej do reprezentowania produktów, lokalnej klasy wewnętrznej do obliczania rabatów oraz anonimowej klasy wewnętrznej do formatowania końcowego paragonu.
Rozmieścisz kod w dwóch plikach:
ShoppingCart.java: Utwórz klasę zewnętrzną, która zarządza zakupami. Twoja klasa ShoppingCart powinna mieć prywatne polecustomerName(String).Wewnątrz ShoppingCart zdefiniuj zagnieżdżoną klasę wewnętrzną o nazwie
Itemz dwoma prywatnymi polami:name(String) iprice(double). Dodaj do Item konstruktor inicjalizujący oba pola oraz metodęgetDetails(), która zwraca:[name]: $[price]Twoja klasa ShoppingCart potrzebuje konstruktora przyjmującego nazwę klienta oraz metody o nazwie
checkout(), która przyjmuje Item i procentową wartość rabatu (double). Wewnątrzcheckout():- Utwórz lokalną klasę wewnętrzną o nazwie
DiscountCalculatorz metodąapplyDiscount(double originalPrice), która zwraca cenę po zastosowaniu procentowego rabatu. - Użyj klasy lokalnej, aby obliczyć cenę końcową.
- Utwórz anonimową klasę wewnętrzną, która implementuje interfejs
Formatter(zdefiniujesz go), aby sformatować paragon. Anonimowa klasa powinna nadpisywać metodęformat(), która zwraca wielowierszowy ciąg znaków z paragonem.
Metoda
checkout()powinna zwracać sformatowany ciąg znaków z paragonem w dokładnie takim formacie:--- Receipt --- Customer: [customerName] Item: [item details from getDetails()] Discount: [discount]% Final Price: $[calculated price]- Utwórz lokalną klasę wewnętrzną o nazwie
Formatter.java: Zdefiniuj prosty interfejs z jedną metodą:String format(). Ten interfejs zostanie zaimplementowany przez anonimową klasę wewnętrzną w metodzie checkout.Main.java: Ożyw swój koszyk zakupowy! Otrzymasz trzy dane wejściowe: nazwę klienta (String), nazwę produktu (String) i cenę produktu (double). Procentowy rabat jest stały i wynosi10.0. Utwórz ShoppingCart z nazwą klienta. Następnie utwórz Item, używając składni odwołania do klasy zewnętrznej:cart.new Item(name, price). Wywołajcheckout()z produktem i rabatem, a następnie wypisz zwrócony paragon.
Otrzymasz kolejno trzy dane wejściowe: nazwę klienta, nazwę produktu i cenę produktu.
Na przykład, jeśli dane wejściowe to Alice, Laptop i 1000.0, cena końcowa po rabacie w wysokości 10% wyniesie 900.0.
Zwróć uwagę, jak każdy typ klasy wewnętrznej służy innemu celowi: klasa zagnieżdżona grupuje powiązane dane z klasą zewnętrzną, klasa lokalna hermetyzuje logikę potrzebną tylko w obrębie jednej metody, a klasa anonimowa zapewnia szybką, jednorazową implementację interfejsu!
Spróbuj swoich sił
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Wczytaj dane wejściowe
String customerName = scanner.nextLine();
String itemName = scanner.nextLine();
double itemPrice = scanner.nextDouble();
double discount = 10.0;
// TODO: Utwórz ShoppingCart z nazwą klienta
// TODO: Utwórz Item za pomocą: cart.new Item(itemName, itemPrice)
// TODO: Wywołaj checkout() z przedmiotem i rabatem, a następnie wypisz wynik
}
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneWprowadzenie do programowania obiektowegoKlasy a obiektySłowo kluczowe thisMetodyPola (atrybuty)Metoda konstruktoraPrzeciążanie konstruktoraPodsumowanie — prosty kalkulator4Dziedziczenie
Podstawy dziedziczenia (extends)Słowo kluczowe superPrzesłanianie metod (@Override)Łańcuch wywołań konstruktorówKlasa ObjectDziedziczenie jedno- i wielopoziomoweDlaczego Java nie obsługuje wielodziedziczenia klasPodsumowanie – hierarchia pracowników7Metody specjalne i klasa Object
Metoda toString()equals() i hashCode()Metoda clone()compareTo() i ComparableInterfejs ComparatorPodsumowanie — własne sortowanie10Obsługa wyjątków w programowaniu obiektowym
Hierarchia klas wyjątkówWłasne wyjątkiBłędy sprawdzane a niesprawdzaneWzorzec try-with-resourcesPodsumowanie – zweryfikowany użytkownik2Modyfikatory dostępu i enkapsulacja
Przegląd poziomów dostępuMetody pobierające i ustawiająceUkrywanie informacjiSłowo kluczowe finalPodsumowanie — menedżer kont bankowych5Polimorfizm
Podstawy przeciążania metodPrzesłanianie metod (w czasie wykonywania)Rzutowanie w górę i w dółOperator instanceofKlasy i metody abstrakcyjnePowtórka — kalkulator figur8Zaawansowane koncepcje OOP
Kompozycja a dziedziczenieAgregacja a kompozycjaKlasy wewnętrzne, zagnieżdżone i anonimoweEnumy i metody enumówRekordy (Java 16+)Klasy zapieczętowane (Java 17+)11Wzorce projektowe — część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec BuilderWzorzec ObserverWzorzec Strategy3Właściwości klas i składowe statyczne
Zmienne instancji a zmienne statyczneMetody statyczneBloki statyczneStałe (static final)Powtórka – Counter i Utility6Interfejsy i klasy abstrakcyjne
Wprowadzenie do interfejsówImplementowanie interfejsówImplementacja wielu interfejsówMetody domyślne i statyczne w interfejsieKlasy abstrakcyjne a interfejsyInterfejsy funkcyjnePodsumowanie — system płatnościPoćwicz samodzielnie: Kompilator Java online