Menu
Coddy logo textTech

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: Hello

Lokalna 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.

challenge icon

Wyzwanie

Łatwy

Zbudujmy 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 pole customerName (String).

    Wewnątrz ShoppingCart zdefiniuj zagnieżdżoną klasę wewnętrzną o nazwie Item z dwoma prywatnymi polami: name (String) i price (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ątrz checkout():

    1. Utwórz lokalną klasę wewnętrzną o nazwie DiscountCalculator z metodą applyDiscount(double originalPrice), która zwraca cenę po zastosowaniu procentowego rabatu.
    2. Użyj klasy lokalnej, aby obliczyć cenę końcową.
    3. 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]
  • 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 wynosi 10.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łaj checkout() 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
        
    }
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Java online