ProgrammazioneSviluppatore Java

Come funziona il meccanismo final in Java — per variabili, metodi e classi, e quali effetti inaspettati può causare un uso improprio di questo modificatore?

Supera i colloqui con l'assistente IA Hintsage

Risposta.

final in Java viene utilizzato per:

  • Variabili — diventa immutabile dopo l'inizializzazione;
  • Metodi — non può essere sovrascritto nelle sottoclassi;
  • Classi — non possono essere create sottoclassi.

Caratteristiche e dettagli:

  • Una variabile final deve essere inizializzata al momento della dichiarazione o nel costruttore;
  • Un riferimento final non consente di cambiare il riferimento, ma l'oggetto referenziato può essere modificato (se non è immutabile!);
  • L'ereditarietà da una classe final (come String, Math) non è possibile;
  • Non è possibile sovrascrivere un metodo final, anche se la classe è ereditata.
final class A {} // non è possibile fare class B extends A class Parent { final void foo() { } } class Child extends Parent { // void foo() {} // errore: non è possibile sovrascrivere foo } final int COUNT = 10;

Domanda trabocchetto.

È possibile cambiare lo stato dell'oggetto a cui fa riferimento una variabile final?

Risposta: Sì, se si tratta di un oggetto non immutabile. Ad esempio:

final List<String> names = new ArrayList<>(); names.add("Vasya"); // Questo è consentito — l'oggetto referenziato viene modificato, ma non il riferimento stesso names = new ArrayList<>(); // Errore di compilazione

Esempi di errori reali dovuti alla mancanza di conoscenza dei dettagli dell'argomento.


Storia

Nel sistema di logging, l'unico final Logger veniva passato in tutto il codice, ritenendo che l'oggetto fosse completamente protetto – ma le impostazioni venivano modificate tramite un metodo pubblico, rompendo la configurazione a livello globale.

Storia

In una classe utility sono stati creati campi final di tipo List, e poi si è cercato di restituirli all'esterno per uso generale. Il codice esterno modificava tranquillamente il contenuto della lista tramite il riferimento ricevuto — final non protegge affatto da questo.

Storia

È emersa la necessità di espandere l'API esterna, costruita su classi final. A causa di esse, l'estensione si è rivelata impossibile, costringendo a duplicare la logica e mantenere due rami indipendenti del prodotto, complicando le migrazioni.