CodeGym /Kursy /JAVA 25 SELF /Klasy lokalne: deklaracja wewnątrz metod

Klasy lokalne: deklaracja wewnątrz metod

JAVA 25 SELF
Poziom 16 , Lekcja 3
Dostępny

1. Klasa lokalna

Klasa lokalna — to klasa zadeklarowana wewnątrz metody (a nawet wewnątrz bloku kodu, na przykład w pętli lub w bloku if). Jest widoczna i dostępna tylko w tej metodzie, a poza nią — jakby w ogóle nie istniała. W odróżnieniu od klas anonimowych klasa lokalna ma nazwę (choćby prostą), może zawierać kilka metod, a nawet pola.

Jeśli posłużyć się analogią — wyobraź sobie, że przygotowujesz złożone danie według przepisu. W jednym z kroków musisz zrobić specjalny sos używany tylko w tym przepisie i nigdzie indziej. Zamiast publikować osobną książkę kucharską dla jednego sosu, opisujesz jego przygotowanie bezpośrednio w głównym przepisie. Klasa lokalna działa tak samo — to „przepis w przepisie”, potrzebny tylko w jednej konkretnej metodzie.

Przykład deklaracji klasy lokalnej

public class Outer {
    void someMethod() {
        class Local {
            void print() {
                System.out.println("Hello from Local!");
            }
        }
        Local local = new Local();
        local.print();
    }
}

Tutaj klasa Local jest zadeklarowana wewnątrz metody someMethod klasy Outer. Istnieje tylko w tej metodzie i nie jest widoczna z zewnątrz.

Składnia klas lokalnych

Deklaracja klasy lokalnej przypomina zwykłą deklarację klasy, tyle że odbywa się wewnątrz metody:

void myMethod() {
    class MyLocalClass {
        void doSomething() {
            System.out.println("Klasa lokalna działa!");
        }
    }

    MyLocalClass obj = new MyLocalClass();
    obj.doSomething();
}

Specyfika składni:

  • Klasę lokalną można zadeklarować wewnątrz dowolnej metody, konstruktora lub nawet bloku inicjalizacyjnego.
  • Nazwa klasy lokalnej jest obowiązkowa (w przeciwieństwie do klas anonimowych).
  • Klasy lokalnej nie można zadeklarować z modyfikatorami dostępu (public, private, protected) ani z modyfikatorem static.
  • Klasa lokalna może implementować interfejsy lub dziedziczyć po innych klasach.

Porównanie typów klas:

public class Example {
// 1. Zwykła klasa (w osobnym pliku)
// class RegularClass { ... }

    // 2. Klasa wewnętrzna (w klasie)
    class InnerClass { }
    
    // 3. Statyczna klasa zagnieżdżona (w klasie)
    static class NestedClass { }
    
    void method() {
        // 4. Klasa lokalna (w metodzie)
        class LocalClass { }
        
        // 5. Klasa anonimowa (w metodzie, bez nazwy)
        Object obj = new Object() { };
    }
}

Klasa lokalna = nazwana klasa tylko dla tej metody!

2. Cechy klas lokalnych

Dostęp do zmiennych metody

Klasa lokalna może odwoływać się do:

  • Pól klasy zewnętrznej (nawet jeśli są private).
  • Tylko do zmiennych final lub effectively final otaczającej metody.

Co to znaczy „effectively final”?
To zmienna, której wartość nie zmienia się po inicjalizacji. Na przykład:

void foo() {
    int x = 5; // effectively final
    class L { void print() { System.out.println(x); } }
}

Gdybyś później napisał(a) x = 10;, kompilator zgłosiłby błąd.

Dlaczego tak jest?

Ma to związek z tym, jak Java realizuje klasy lokalne „pod maską”: zmienne metody są w istocie kopiowane do wnętrza klasy lokalnej, a nie przechowywane przez referencję. Gdyby zmienna mogła się zmieniać, klasa lokalna mogłaby pracować na nieaktualnej kopii — a to prosta droga do błędów i bólu głowy.

Przykład dostępu do zmiennych

public class Outer {
    private String secret = "sekret!";

    void revealSecret() {
        String greeting = "Cześć,"; // effectively final

        class Revealer {
            void printSecret() {
                System.out.println(greeting + " " + secret);
            }
        }

        Revealer revealer = new Revealer();
        revealer.printSecret();
    }
}

3. Zastosowania klas lokalnych: po co i kiedy?

Klasy lokalne to narzędzie nie na każdy dzień, ale czasem czynią kod czystszym i bardziej zwięzłym. Oto kiedy warto ich używać:

  • Logika pomocnicza, specyficzna dla jednej metody.
    Na przykład, gdy w metodzie trzeba zaimplementować złożone sortowanie, filtrowanie lub tymczasową strukturę danych.
  • Enkapsulacja, aby nie „zaśmiecać” klasy zbędnymi typami.
    Jeśli klasa jest potrzebna tylko w jednym miejscu, po co czynić ją widoczną w całej klasie lub pakiecie?
  • Implementacja wzorców projektowych, gdzie obiekty pomocnicze są potrzebne tylko lokalnie.

Przykład: sortowanie za pomocą klasy lokalnej

Załóżmy, że masz listę imion i chcesz posortować je według długości. Można utworzyć lokalną klasę-komparator:

import java.util.*;

public class NameSorter {
    public void sortNames(List<String> names) {
        class LengthComparator implements Comparator<String> {
            @Override
            public int compare(String a, String b) {
                return Integer.compare(a.length(), b.length());
            }
        }
        Collections.sort(names, new LengthComparator());
    }
}

Przykład: tymczasowa struktura

Czasem trzeba stworzyć pomocniczą strukturę do przetwarzania danych w jednej metodzie:

void processScores(int[] scores) {
    class ScoreInfo {
        int score;
        boolean passed;

        ScoreInfo(int score) {
            this.score = score;
            this.passed = score >= 60;
        }
    }

    for (int s : scores) {
        ScoreInfo info = new ScoreInfo(s);
        System.out.println("Ocena: " + info.score + ", zaliczone: " + info.passed);
    }
}

4. Klasy lokalne vs. klasy anonimowe

Czasem pojawia się pytanie: po co w ogóle klasy lokalne, skoro są anonimowe? Przyjrzyjmy się.

  • Klasa lokalna — to nazwana klasa, której można używać wielokrotnie w obrębie metody; można dodać do niej pola, wiele metod, a nawet klasy zagnieżdżone.
  • Klasa anonimowa — to jednorazowa implementacja interfejsu lub klasy bazowej, bez nazwy, zwykle z jedną metodą (lub nadpisaniem jednego–dwóch metod).

Kiedy używać:

  • Jeśli logika jest prosta i potrzebna tylko raz — użyj klasy anonimowej.
  • Jeśli potrzebujesz więcej metod lub pól, albo klasa będzie używana w kilku miejscach metody — użyj klasy lokalnej.

Przykład porównania

Klasa anonimowa:

Runnable r = new Runnable() {
    @Override
    public void run() {
        System.out.println("Szybko i anonimowo!");
    }
};

Klasa lokalna:

void doWork() {
    class MyWorker implements Runnable {
        @Override
        public void run() {
            System.out.println("Jestem klasą lokalną!");
        }
    }
    MyWorker worker = new MyWorker();
    worker.run();
}

5. Ograniczenia i cechy klas lokalnych

  • Modyfikatory dostępu: Klasy lokalnej nie można zadeklarować jako public, private ani protected. Nie można też dodać static.
  • Składowe statyczne: Klasa lokalna nie może zawierać składowych statycznych poza stałymi (static final).
  • Zakres widoczności: Klasa lokalna jest widoczna tylko w tym bloku, w którym została zadeklarowana.
  • Użycie parametrów generycznych: Klasa lokalna może być generyczna, jeśli jest taka potrzeba.
  • Zagnieżdżanie: Można deklarować klasy lokalne wewnątrz innych klas lokalnych (lecz lepiej nie nadużywać tego, bo kod zacznie przypominać matrioszkę).

6. Klasy lokalne w rzeczywistej aplikacji

Kontynuujmy rozwijanie naszej aplikacji edukacyjnej. Załóżmy, że mamy klasę Quiz, która zadaje użytkownikowi pytania i sprawdza odpowiedzi. Chcemy wewnątrz metody sprawdzającej utworzyć tymczasową klasę, która będzie przechowywać odpowiedź użytkownika i status weryfikacji.

Przykład: użycie klasy lokalnej w aplikacji

import java.util.Scanner;

public class Quiz {
    private String question = "Stolica Francji?";
    private String correctAnswer = "Paryż";

    public void runQuiz() {
        Scanner scanner = new Scanner(System.in);

        System.out.println(question);
        String userAnswer = scanner.nextLine();

        // Lokalna klasa do przechowywania wyniku
        class AnswerResult {
            String answer;
            boolean isCorrect;

            AnswerResult(String answer) {
                this.answer = answer;
                this.isCorrect = answer.equalsIgnoreCase(correctAnswer);
            }

            void printResult() {
                if (isCorrect) {
                    System.out.println("Poprawnie!");
                } else {
                    System.out.println("Niepoprawnie, prawidłowa odpowiedź: " + correctAnswer);
                }
            }
        }

        AnswerResult result = new AnswerResult(userAnswer);
        result.printResult();
    }
}

Tutaj klasa AnswerResult istnieje tylko wewnątrz metody runQuiz i nigdzie indziej nie jest potrzebna. To świetny przykład klasy lokalnej w praktyce!

7. Typowe błędy podczas pracy z klasami lokalnymi

Błąd nr 1: próba odwołania się do zmiennej metody, która nie jest final ani effectively final.
Jeśli zadeklarujesz zmienną w metodzie, a następnie zmienisz jej wartość po wykorzystaniu w klasie lokalnej, kompilator natychmiast zgłosi błąd. Zawsze dbaj, by takie zmienne nie zmieniały się po inicjalizacji.

Błąd nr 2: chęć dodania modyfikatorów dostępu lub static.
Klasy lokalnej nie można zadeklarować z modyfikatorami public, private, protected ani static. Jeśli spróbujesz — kompilator będzie przeciw.

Błąd nr 3: próba użycia klasy lokalnej poza metodą.
Klasa lokalna żyje tylko w tej metodzie (lub bloku), w której została zadeklarowana. Z innych metod lub spoza klasy nie jest dostępna.

Błąd nr 4: nadużywanie klas lokalnych do złożonej logiki.
Jeśli twoja klasa lokalna staje się zbyt duża, zawiera wiele metod lub pól — najpewniej warto wydzielić ją do osobnej klasy. Klasy lokalne są dobre do zwięzłych, pomocniczych zadań.

1
Zadanie
JAVA 25 SELF, poziom 16, lekcja 3
Niedostępne
Interaktywna tablica ogłoszeń: tymczasowe powitanie 👋
Interaktywna tablica ogłoszeń: tymczasowe powitanie 👋
1
Zadanie
JAVA 25 SELF, poziom 16, lekcja 3
Niedostępne
Strażnik tajemnicy: kontrolowane ujawnienie sekretu 🔐
Strażnik tajemnicy: kontrolowane ujawnienie sekretu 🔐
1
Zadanie
JAVA 25 SELF, poziom 16, lekcja 3
Niedostępne
Specjalistyczny kalkulator: natychmiastowe wyświetlanie sumy ➕
Specjalistyczny kalkulator: natychmiastowe wyświetlanie sumy ➕
1
Zadanie
JAVA 25 SELF, poziom 16, lekcja 3
Niedostępne
Analizator danych tekstowych: krótki raport dotyczący imienia 📊
Analizator danych tekstowych: krótki raport dotyczący imienia 📊
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION