1. Classe locale
Una classe locale è una classe dichiarata all’interno di un metodo (o persino all’interno di un blocco di codice, ad esempio in un ciclo o in un blocco if). È visibile e accessibile solo all’interno di quel metodo e, al di fuori di esso, è come se non esistesse. A differenza delle classi anonime, una classe locale ha un nome (anche semplice), può contenere più metodi e persino campi.
Se facciamo un’analogia — immagina di preparare un piatto complesso seguendo una ricetta. In uno dei passaggi devi preparare una salsa speciale che si usa solo in quella ricetta e da nessun’altra parte. Invece di pubblicare un libro di ricette a parte per una sola salsa, ne descrivi la preparazione direttamente all’interno della ricetta principale. La classe locale funziona allo stesso modo — è una «ricetta dentro la ricetta», necessaria solo in uno specifico metodo.
Esempio di dichiarazione di una classe locale
public class Outer {
void someMethod() {
class Local {
void print() {
System.out.println("Hello from Local!");
}
}
Local local = new Local();
local.print();
}
}
Qui la classe Local è dichiarata all’interno del metodo someMethod della classe Outer. Esiste solo in questo metodo e non è visibile dall’esterno.
Sintassi delle classi locali
La dichiarazione di una classe locale è simile alla dichiarazione di una classe normale, solo che avviene all’interno di un metodo:
void myMethod() {
class MyLocalClass {
void doSomething() {
System.out.println("La classe locale funziona!");
}
}
MyLocalClass obj = new MyLocalClass();
obj.doSomething();
}
Particolarità della sintassi:
- Una classe locale può essere dichiarata all’interno di qualsiasi metodo, costruttore o anche di un blocco di inizializzazione.
- Il nome della classe locale è obbligatorio (a differenza delle classi anonime).
- Una classe locale non può essere dichiarata con modificatori di accesso (public, private, protected) né con il modificatore static.
- Una classe locale può implementare interfacce o estendere altre classi.
Confronto tra tipi di classi:
public class Example {
// 1. Classe normale (in un file separato)
// class RegularClass { ... }
// 2. Classe interna (dentro la classe)
class InnerClass { }
// 3. Classe annidata statica (dentro la classe)
static class NestedClass { }
void method() {
// 4. Classe locale (nel metodo)
class LocalClass { }
// 5. Classe anonima (nel metodo, senza nome)
Object obj = new Object() { };
}
}
Classe locale = una classe con nome solo per questo metodo!
2. Caratteristiche delle classi locali
Accesso alle variabili del metodo
Una classe locale può accedere a:
- Ai campi della classe esterna (anche se sono private).
- Solo alle variabili final o effectively final del metodo circostante.
Che cos’è «effectively final»?
È una variabile il cui valore non cambia dopo l’inizializzazione. Ad esempio:
void foo() {
int x = 5; // effectively final
class L { void print() { System.out.println(x); } }
}
Se in seguito scrivessi x = 10;, il compilatore segnalerebbe un errore.
Perché?
Ciò dipende da come Java implementa le classi locali «sotto il cofano»: le variabili del metodo vengono di fatto copiate all’interno della classe locale, non mantenute per riferimento. Se una variabile potesse cambiare, la classe locale potrebbe lavorare con una copia obsoleta — ed è una strada verso bug e mal di testa.
Esempio di accesso alle variabili
public class Outer {
private String secret = "segreto!";
void revealSecret() {
String greeting = "Ciao,"; // effectively final
class Revealer {
void printSecret() {
System.out.println(greeting + " " + secret);
}
}
Revealer revealer = new Revealer();
revealer.printSecret();
}
}
3. Uso delle classi locali: perché e quando?
Le classi locali non sono uno strumento da usare ogni giorno, ma talvolta rendono il codice più pulito e compatto. Ecco quando conviene usarle:
- Logica di supporto, specifica di un singolo metodo.
Ad esempio, se in un metodo occorre implementare un ordinamento complesso, un filtraggio o una struttura dati temporanea. - Incapsulamento, per non «sporcare» la classe con tipi superflui.
Se una classe serve in un solo punto, perché renderla visibile all’intera classe o al package? - Implementazione di pattern progettuali in cui oggetti di supporto servono solo localmente.
Esempio: ordinamento con una classe locale
Supponiamo di avere un elenco di nomi e di volerli ordinare per lunghezza. Si può creare una classe locale comparatore:
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());
}
}
Esempio: struttura temporanea
A volte è necessario creare una struttura di supporto per elaborare i dati all’interno di un unico metodo:
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("Punteggio: " + info.score + ", superato: " + info.passed);
}
}
4. Classi locali vs classi anonime
A volte nasce la domanda: a cosa servono le classi locali, se ci sono quelle anonime? Vediamolo.
- Classe locale — è una classe con nome, che puoi riutilizzare più volte nel metodo; puoi aggiungervi campi, più metodi e perfino classi annidate.
- Classe anonima — è un’implementazione usa e getta di un’interfaccia o di una classe base, senza nome, di solito con un solo metodo (o con l’override di uno‑due metodi).
Quando usarle:
- Se la logica è semplice e serve una sola volta — usa una classe anonima.
- Se servono più metodi o campi, o se la classe verrà usata in più punti del metodo — usa una classe locale.
Esempio di confronto
Classe anonima:
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Veloce e anonimo!");
}
};
Classe locale:
void doWork() {
class MyWorker implements Runnable {
@Override
public void run() {
System.out.println("Sono una classe locale!");
}
}
MyWorker worker = new MyWorker();
worker.run();
}
5. Limitazioni e particolarità delle classi locali
- Modificatori di accesso: Una classe locale non può essere dichiarata come public, private o protected. Non è possibile aggiungere neanche static.
- Membri statici: Una classe locale non può contenere membri statici, tranne le costanti (static final).
- Ambito di visibilità: Una classe locale è visibile solo nel blocco in cui è dichiarata.
- Uso di parametri generics: Una classe locale può essere generica, se necessario.
- Annidamento: È possibile dichiarare classi locali all’interno di altre classi locali (ma meglio non esagerare, altrimenti il codice sembrerà una matrioska).
6. Classi locali in un’applicazione reale
Continuiamo a far evolvere la nostra app didattica. Supponiamo di avere una classe Quiz che pone domande all’utente e verifica le risposte. Vogliamo, all’interno del metodo di verifica, creare una classe temporanea che memorizzi la risposta dell’utente e lo stato della verifica.
Esempio: uso di una classe locale nell’applicazione
import java.util.Scanner;
public class Quiz {
private String question = "Capitale della Francia?";
private String correctAnswer = "Parigi";
public void runQuiz() {
Scanner scanner = new Scanner(System.in);
System.out.println(question);
String userAnswer = scanner.nextLine();
// Classe locale per memorizzare il risultato
class AnswerResult {
String answer;
boolean isCorrect;
AnswerResult(String answer) {
this.answer = answer;
this.isCorrect = answer.equalsIgnoreCase(correctAnswer);
}
void printResult() {
if (isCorrect) {
System.out.println("Corretto!");
} else {
System.out.println("Errato, risposta corretta: " + correctAnswer);
}
}
}
AnswerResult result = new AnswerResult(userAnswer);
result.printResult();
}
}
Qui la classe AnswerResult esiste solo all’interno del metodo runQuiz e non serve altrove. È un ottimo esempio di classe locale nella pratica!
7. Errori comuni con le classi locali
Errore n. 1: tentativo di accedere a una variabile del metodo che non è final o effectively final.
Se dichiari una variabile nel metodo e poi ne modifichi il valore dopo averla usata nella classe locale, il compilatore genererà subito un errore. Assicurati sempre che tali variabili non cambino dopo l’inizializzazione.
Errore n. 2: voler aggiungere modificatori di accesso o static.
Una classe locale non può essere dichiarata con i modificatori public, private, protected o static. Se ci provi, il compilatore protesterà.
Errore n. 3: tentare di usare una classe locale fuori dal metodo.
Una classe locale vive solo nel metodo (o blocco) in cui è dichiarata. Da altri metodi o dall’esterno della classe non è accessibile.
Errore n. 4: abusare delle classi locali per logiche complesse.
Se la tua classe locale diventa troppo grande, contiene molti metodi o campi, probabilmente è il caso di estrarla in una classe separata. Le classi locali sono adatte a compiti compatti e di supporto.
GO TO FULL VERSION