W3docs

Classi Locali in Java

Dichiara classi nel corpo di un metodo in Java con le classi locali, con accesso alle variabili locali effettivamente finali.

Una classe locale è una classe dichiarata all'interno del corpo di un metodo (o di un costruttore, o di un blocco di inizializzazione). Il suo scope è limitato al blocco in cui è dichiarata — nulla al di fuori di quel blocco può farvi riferimento per nome. Per il resto si comporta molto come una classe interna, con accesso ai membri dell'istanza che la racchiude e alle variabili locali effettivamente finali del metodo.

Le classi locali sono rare nel Java moderno. Le lambda hanno sostituito i loro casi d'uso principali per le callback a un solo metodo, e le classi anonime sono generalmente più brevi per implementazioni una tantum. Ma una classe locale è occasionalmente la soluzione più pulita quando hai bisogno di un tipo con nome che partecipa alla logica del metodo che la racchiude e non è utile altrove.

Se non hai ancora incontrato gli altri membri della famiglia delle classi nidificate, i capitoli sulla panoramica delle classi nidificate e sulle classi interne definiscono i termini usati di seguito.

Dichiararne una

Una dichiarazione di classe all'interno del corpo di un metodo:

public void process(List<String> lines) {
  class Counter {
    int n = 0;
    void inc() { n++; }
  }

  Counter c = new Counter();
  for (String s : lines) if (!s.isBlank()) c.inc();

  System.out.println("non-blank lines: " + c.n);
}

Al di fuori di process, nessuno può vedere Counter — il suo nome non esiste oltre la graffa di chiusura del metodo. All'interno, la si usa come qualsiasi altra classe.

Perché sceglierla rispetto a una classe anonima?

Una classe locale ha un nome reale, il che offre alcune cose che una classe anonima non può avere:

  • Un costruttore. Puoi assegnarle parametri, scrivere validazioni e avere più costruttori.
  • Istanze multiple. Poiché la classe ha un nome, puoi scrivere new Counter() più di una volta. La definizione di una classe anonima crea una sola istanza per espressione.
  • Autoreferenza. Counter può prendere se stessa come parametro, contenere un campo Counter next, ricorrere — tutte le cose che i tipi con nome possono fare.
  • Leggibilità. Un nome come Counter documenta a cosa serve il helper. I blocchi anonimi new Object() { ... } sono più difficili da scorrere velocemente.

Accesso allo stato che la racchiude

Le classi locali possono:

  • Leggere e scrivere i campi dell'istanza che le racchiude (se il metodo che le racchiude è non statico).
  • Chiamare i metodi di istanza della classe che le racchiude.
  • Leggere — ma non riassegnare — le variabili locali e i parametri finali o effettivamente finali del metodo che le racchiude.
void run(int multiplier) {
  class Scaler {
    int scale(int x) { return x * multiplier; }   // captures the parameter
  }
  System.out.println(new Scaler().scale(7));
}

multiplier viene catturato nella classe sintetica come campo. Il requisito "effettivamente finale" è la ragione — se multiplier potesse essere riassegnato all'interno di run dopo che Scaler aveva catturato il suo vecchio valore, i due sarebbero silenziosamente in disaccordo.

In un metodo statico

In un metodo static, la classe locale è implicitamente statica — non ha un'istanza che la racchiude a cui fare riferimento:

public static int demo() {
  class Helper {
    int square(int n) { return n * n; }
  }
  return new Helper().square(7);
}

Non c'è Outer.this qui, perché il metodo che la racchiude è statico. Helper può ancora catturare variabili locali effettivamente finali da demo, ma non i campi di istanza di un oggetto che la racchiude e che non esiste.

Quando usarla davvero

Casi concreti in cui una classe locale supera genuinamente le alternative:

  • Il helper ha più di un metodo, e preferisci vedere i metodi raggruppati insieme con un nome chiaro piuttosto che distribuiti nel boilerplate delle classi anonime.
  • Il helper ha bisogno di un costruttore per validare la propria configurazione.
  • Il helper è ricorsivo o contiene riferimenti ad altre istanze del proprio tipo.

Se una lambda o una classe anonima breve sarebbe sufficiente, preferisci quella. Le classi locali sono uno strumento di nicchia.

Modificatori

Una classe locale accetta solo un insieme ristretto di modificatori:

  • final — contrassegna la classe come non estendibile. Ha già nessun nome per il codice esterno da estendere, quindi questo è principalmente una dichiarazione di intenti.
  • abstract — lecito ma raro: sarebbe necessaria una seconda classe locale nello stesso blocco per estenderla.
  • I modificatori di accesso (public, protected, private) non sono consentiti. Una classe locale vive interamente all'interno di un blocco, quindi non esiste uno scope al di fuori di quel blocco da cui la visibilità potrebbe essere controllata.

Non può nemmeno essere dichiarata static direttamente. Il fatto che catturi un'istanza che la racchiude dipende dal fatto che il metodo circostante sia statico, come mostrato sopra — non da una parola chiave.

Un esempio pratico

java— editable, runs on the server

Cosa viene dopo

Questo conclude il tour delle classi nidificate — e la maggior parte del meccanismo OOP classico di Java. La prossima sezione della Parte 6 si sposta nei tipi di classi speciali: tipi che Java fornisce per forme specifiche di dati, a partire da enum per insiemi fissi di costanti. Continua con Java enum.

Esercitazione

Pratica
Perché potresti scegliere una classe locale rispetto a una classe anonima per lo stesso compito?
Perché potresti scegliere una classe locale rispetto a una classe anonima per lo stesso compito?
Was this page helpful?