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ń.
GO TO FULL VERSION