Classi e Metodi final in Java
In Java la parola chiave final
ha tre usi.
Il primo utilizzo lo abbiamo già analizzato: può essere utilizzata per creare l'equivalente di una costante con nome.
Gli altri due usi di final
si applicano all'ereditarietà e li studieremo in questa lezione.
final
può essere utilizzato per prevenire l'override di metodi e l'ereditarietà di classi.- I metodi
final
non possono essere sovrascritti e possono migliorare le prestazioni grazie all'inserimento in linea. - Le classi
final
non possono essere sottoclassate, il che implica che tutti i loro metodi sono automaticamentefinal
. - È illegale dichiarare una classe sia
abstract
chefinal
.
Uso di final
per Prevenire l'Override di Metodi
Sebbene l'override dei metodi sia una delle funzionalità più potenti di Java, ci saranno momenti in cui si vorrà impedirne l'applicazione.
Per vietare che un metodo venga sovrascritto, si deve specificare final
come modificatore all'inizio della sua dichiarazione. I metodi dichiarati come final
non possono essere sovrascritti ossia non se ne può effettuare l'override.
Il seguente frammento illustra final
:
class A {
final void metodo() {
System.out.println("Questo è un metodo final.");
}
}
class B extends A {
void metodo() { // ERRORE! Impossibile sovrascrivere.
System.out.println("Illegale!");
}
}
Poiché metodo()
è dichiarato come final
, non può essere sovrascritto in B
. Se si tenta di farlo, risulterà in un errore di compilazione.
I metodi dichiarati come final
possono talvolta fornire un miglioramento delle prestazioni: il compilatore è libero di inserire in linea le chiamate a questi metodi perché sa a priori che non verranno sovrascritti da una sottoclasse.
Quando viene chiamato un piccolo metodo final
, spesso il compilatore Java può copiare il bytecode per la subroutine direttamente nel codice compilato del metodo chiamante, eliminando così il costoso overhead associato a una chiamata di metodo. L'inserimento in linea è un'opzione disponibile solo con i metodi final
.
Normalmente, Java risolve le chiamate ai metodi in modo dinamico, in fase di esecuzione. Questo è chiamato late binding. Tuttavia, poiché i metodi final
non possono essere sovrascritti, una chiamata a uno di essi può essere risolta in fase di compilazione. Questo è chiamato early binding.
Uso di final
per Prevenire l'Ereditarietà di Classi
A volte si vorrà impedire che una classe venga ereditata.
Per farlo, si deve far precedere la dichiarazione della classe con final
. Dichiarare una classe come final
implica dichiarare automaticamente tutti i suoi metodi come final
, anch'essi.
Come ci si potrebbe aspettare, è illegale dichiarare una classe sia abstract
che final
, poiché una classe astratta è incompleta di per sé e si affida alle sue sottoclassi per fornire implementazioni complete.
Ecco un esempio di una classe final
:
final class A {
//...
}
// La seguente classe è illegale.
class B extends A { // ERRORE! Impossibile sottoclassare A
//...
}
Come indicano i commenti, è illegale per B
ereditare da A
poiché A
è dichiarata come final
.
Controllo avanzato dell'ereditarietà
A partire da JDK 17, è stata aggiunta a Java la possibilità di sigillare (seal
) una classe. Il sealing offre un controllo preciso sull'ereditarietà. Studieremo questo concetto nelle lezioni future.