Qual è il punto della "classe finale" in Java?


569

Sto leggendo un libro su Java e dice che puoi dichiarare l'intera classe come final. Non riesco a pensare a nulla in cui lo userei.

Sono appena nuovo alla programmazione e mi chiedo se i programmatori lo utilizzino davvero nei loro programmi . Se lo fanno, quando lo usano in modo che io possa capirlo meglio e sapere quando usarlo.

Se Java è orientato agli oggetti e si dichiara una classe final, non si ferma l'idea che la classe abbia le caratteristiche degli oggetti?

Risposte:


531

Prima di tutto, raccomando questo articolo: Java: quando creare una classe finale


Se lo fanno, quando lo usano in modo che io possa capirlo meglio e sapere quando usarlo.

Una finalclasse è semplicemente una classe che non può essere estesa .

(Non significa che tutti i riferimenti agli oggetti della classe si comporterebbero come se fossero stati dichiarati come final.)

Quando è utile dichiarare una classe come finale è trattata nelle risposte a questa domanda:

Se Java è orientato agli oggetti e si dichiara una classe final, non si ferma l'idea che la classe abbia le caratteristiche degli oggetti?

In un certo senso si.

Contrassegnando una classe come finale disabiliti una funzione potente e flessibile della lingua per quella parte del codice. Tuttavia, alcune classi non dovrebbero (e in alcuni casi non possono ) essere progettate per tenere conto della sottoclasse in modo corretto. In questi casi ha senso contrassegnare la classe come finale, anche se limita OOP. (Ricorda tuttavia che una classe finale può ancora estendere un'altra classe non finale.)


39
Per aggiungere alla risposta, uno dei principi di Effective Java è quello di favorire la composizione rispetto all'eredità. L'uso della parola chiave finale aiuta anche a far rispettare tale principio.
Riggy

9
"Lo fai principalmente per motivi di efficienza e sicurezza." Sento questa osservazione abbastanza spesso (anche Wikipedia lo afferma) ma ancora non capisco il ragionamento alla base di questo argomento. Qualcuno si preoccupa di spiegare come, per esempio, un java.lang non definitivo.String sarebbe finito inefficace o insicuro?
MRA,

27
@MRA Se creo un metodo che accetta una stringa come parametro, suppongo che sia immutabile, perché lo sono le stringhe. Di conseguenza, so di poter chiamare qualsiasi metodo sull'oggetto String in modo sicuro e non modificare la stringa passata. Se dovessi estendere String e cambiare l'implementazione della sottostringa per cambiare la stringa effettiva, l'oggetto String che ti aspettavi fosse immutabile non è più immutabile.
Cruncher,

1
@Sortofabeginner E non appena dici che vuoi che tutti i metodi e i campi String siano definitivi, solo per poter creare una classe con funzionalità aggiuntive ... A quel punto potresti anche creare una classe che ha una stringa e creare metodi che operano su quella stringa.
Cruncher,

1
@Shay final (tra le altre cose) viene utilizzato per rendere immutabile un oggetto, quindi non direi che non hanno nulla a che fare l'uno con l'altro. Vedi qui docs.oracle.com/javase/tutorial/essential/concurrency/…
Celeritas

184

In Java, gli elementi con il finalmodificatore non possono essere modificati!

Ciò include le classi finali, le variabili finali e i metodi finali:

  • Una classe finale non può essere estesa da nessun'altra classe
  • Una variabile finale non può essere riassegnata ad un altro valore
  • Un metodo finale non può essere ignorato

40
La vera domanda è perché , non cosa .
Francesco Menzani,

8
L'affermazione, "In Java, gli elementi con il finalmodificatore non possono essere modificati!", È troppo categorico e, di fatto, non del tutto corretto. Come diceva Grady Booch, "Un oggetto ha stato, comportamento e identità". Sebbene non possiamo cambiare l'identità di un oggetto una volta che il suo riferimento è stato contrassegnato come definitivo, abbiamo la possibilità di cambiarne lo stato assegnando nuovi valori ai suoi non- finalcampi (purché, ovviamente, li abbia.) Chiunque sia la pianificazione di ottenere una certificazione Oracle Java (come 1Z0-808, ecc.) dovrebbe tenerlo presente perché potrebbero esserci domande su questo aspetto nell'esame ...
Igor Soudakevitch,

33

Uno scenario in cui final è importante, quando si desidera impedire l'ereditarietà di una classe, per motivi di sicurezza. Ciò consente di assicurarsi che il codice in esecuzione non possa essere sovrascritto da qualcuno.

Un altro scenario è per l'ottimizzazione: mi sembra di ricordare che il compilatore Java includa alcune chiamate di funzione dalle classi finali. Quindi, se chiami a.x()e viene dichiarato a final, sappiamo in fase di compilazione quale sarà il codice e possiamo incorporare la funzione chiamante. Non ho idea se questo sia effettivamente fatto, ma con il finale è una possibilità.


7
L'allineamento viene normalmente eseguito solo dal compilatore just-in-time in fase di esecuzione. Funziona anche senza final, ma il compilatore JIT ha un po 'più di lavoro da fare per essere sicuro che non ci siano classi estendibili (o che queste classi estendenti non tocchino questo metodo).
Paŭlo Ebermann,


24

L'esempio migliore è

String della classe finale pubblica

che è una classe immutabile e non può essere estesa. Naturalmente, c'è molto di più che rendere la classe finale immutabile.


Hehe, a volte protegge gli sviluppatori Rube Goldergian da se stessi.
Zoidberg,

16

Lettura rilevante: il principio aperto-chiuso di Bob Martin.

Citazione chiave:

Le entità software (classi, moduli, funzioni, ecc.) Devono essere aperte per l'estensione, ma chiuse per la modifica.

La finalparola chiave è il mezzo per imporre questo in Java, sia che venga utilizzato su metodi o su classi.


6
@Sean: la sua dichiarazione non finalrende la classe chiusa per estensione anziché aperta? O lo sto prendendo troppo alla lettera?
Goran Jovic

4
@Goran applicando globalmente la finale, sì. La chiave è applicare selettivamente il finale in luoghi in cui non si desidera apportare modifiche (e ovviamente fornire buoni hook per l'estensione)
Sean Patrick Floyd

26
In OCP, "modifica" si riferisce alla modifica del codice sorgente e "estensione" si riferisce all'eredità dell'implementazione. Pertanto, l'uso di finaluna dichiarazione di classe / metodo non avrebbe senso se si desidera che il codice di implementazione venga chiuso per modifica ma aperto per estensione per ereditarietà.
Rogério,

1
@Rogerio Ho preso in prestito il riferimento (e l'interpretazione) dal Spring Framework Reference (MVC) . IMHO questo ha molto più senso della versione originale.
Sean Patrick Floyd,

L'estensione è morta. Inutili. Decimata. Distrutto. Non mi interessa l'OCP. Non c'è mai una scusa per estendere una lezione.
Josh Woodcock,

15

Se immagini la gerarchia di classi come un albero (come in Java), le classi astratte possono essere solo rami e le classi finali sono quelle che possono essere solo foglie. Le classi che non rientrano in nessuna di queste categorie possono essere sia rami che foglie.

Non vi è alcuna violazione dei principi OO qui, il finale è semplicemente fornire una bella simmetria.

In pratica, vuoi usare final se vuoi che i tuoi oggetti siano immutabili o se stai scrivendo un'API, per segnalare agli utenti dell'API che la classe non è destinata all'estensione.


13

La finalstessa parola chiave significa che qualcosa è definitivo e non dovrebbe essere modificato in alcun modo. Se una classe è marcata, finalnon può essere estesa o sottoclassata. Ma la domanda è: perché segniamo una lezione final? IMO ci sono vari motivi:

  1. Standardizzazione: alcune classi svolgono funzioni standard e non sono pensate per essere modificate, ad es. Classi che svolgono varie funzioni correlate a manipolazioni di stringhe o funzioni matematiche ecc.
  2. Ragioni di sicurezza : a volte scriviamo classi che svolgono varie funzioni di autenticazione e password e non vogliamo che vengano modificate da nessun altro.

Ho sentito che la classe di marcatura finalmigliora l'efficienza, ma francamente non sono riuscito a trovare questo argomento per avere molto peso.

Se Java è orientato agli oggetti e si dichiara una classe finale, non si ferma l'idea che la classe abbia le caratteristiche degli oggetti?

Forse sì, ma a volte questo è lo scopo previsto. A volte lo facciamo per ottenere maggiori benefici in termini di sicurezza ecc. Sacrificando la capacità di questa classe di essere estesa. Ma una classe finale può ancora estendere una classe se necessario.

Da un lato, dovremmo preferire la composizione all'eredità e la finalparola chiave aiuta effettivamente a far rispettare questo principio.


6

Fai attenzione quando fai una lezione "finale". Perché se si desidera scrivere un test unitario per una classe finale, non è possibile sottoclassare questa classe finale per utilizzare la tecnica di interruzione delle dipendenze "Metodo di sottoclasse e sostituzione" descritta nel libro di Michael C. Feathers "Lavorare efficacemente con il codice legacy" . In questo libro, Feathers ha detto: "Seriamente, è facile credere che il sigillato e il finale siano un errore sbagliato, che non avrebbero mai dovuto essere aggiunti ai linguaggi di programmazione. Ma il vero difetto sta a noi. Quando dipendiamo direttamente da biblioteche che sono fuori dal nostro controllo, stiamo solo chiedendo problemi ".


6

final class puoi evitare di interrompere l'API pubblica quando aggiungi nuovi metodi

Supponiamo che nella versione 1 della tua Baseclasse tu faccia:

public class Base {}

e un cliente fa:

class Derived extends Base {
    public int method() { return 1; }
}

Quindi se nella versione 2 vuoi aggiungere un methodmetodo a Base:

class Base {
    public String method() { return null; }
}

si spezzerebbe il codice client.

Se avessimo usato final class Baseinvece, il client non sarebbe stato in grado di ereditare e l'aggiunta del metodo non avrebbe interrotto l'API.


5

Se la classe è contrassegnata final, significa che la struttura della classe non può essere modificata da nulla esterno. Dove questo è il più visibile è quando stai facendo l'eredità polimorfica tradizionale, praticamente class B extends Anon funzionerà. È fondamentalmente un modo per proteggere alcune parti del codice (per quanto possibile) .

Per chiarire, contrassegnare la classe finalnon contrassegna i suoi campi come finale come tali non protegge invece le proprietà dell'oggetto ma la struttura della classe effettiva.


1
Cosa significano le proprietà dell'oggetto? Significa che potrei modificare la variabile membro della classe se la classe viene dichiarata finale? Quindi l'unico scopo della classe finale è prevenire l'ereditarietà.
Adam Lyu,

5

PER INDIRIZZARE IL PROBLEMA DELLA CLASSE FINALE:

Esistono due modi per rendere finale una classe. Il primo è utilizzare la parola chiave final nella dichiarazione di classe:

public final class SomeClass {
  //  . . . Class contents
}

Il secondo modo per rendere finale una classe è dichiarare privati ​​tutti i suoi costruttori:

public class SomeClass {
  public final static SOME_INSTANCE = new SomeClass(5);
  private SomeClass(final int value) {
  }

Contrassegnarlo come finale ti risparmia il problema se scopri che è effettivamente un finale, per dimostrare un'occhiata a questa classe di test. sembra pubblico a prima vista.

public class Test{
  private Test(Class beanClass, Class stopClass, int flags)
    throws Exception{
    //  . . . snip . . . 
  }
}

Sfortunatamente, poiché l'unico costruttore della classe è privato, è impossibile estenderla. Nel caso della classe Test, non vi è alcun motivo per cui la classe debba essere definitiva. La classe Test è un buon esempio di come le classi finali implicite possano causare problemi.

Quindi dovresti segnarlo come finale quando rendi implicitamente una classe finale rendendola privata come costruttore.


4

Una classe finale è una classe che non può essere estesa. Inoltre, i metodi potrebbero essere dichiarati come definitivi per indicare che non possono essere ignorati dalle sottoclassi.

Impedire che la classe venga sottoclassata potrebbe essere particolarmente utile se si scrivono API o librerie e si desidera evitare di essere estesi per modificare il comportamento di base.


4

Un vantaggio di mantenere una classe come finale: -

La classe String viene mantenuta definitiva in modo che nessuno possa sovrascriverne i metodi e modificarne la funzionalità. ad esempio nessuno può cambiare la funzionalità del metodo length (). Restituirà sempre la lunghezza di una stringa.

Lo sviluppatore di questa classe non voleva che nessuno cambiasse funzionalità di questa classe, quindi l'ha mantenuta come definitiva.



3

In java le parole chiave finali vengono utilizzate per le occasioni seguenti.

  1. Variabili finali
  2. Metodi finali
  3. Classi finali

In java le variabili finali non possono essere riassegnate, le classi finali non possono estendersi e i metodi finali non possono essere sostituiti.


1

Le lezioni finali non possono essere estese. Quindi, se vuoi che una classe si comporti in un certo modo e non qualcuno che ignori i metodi (con un codice forse meno efficiente e più dannoso), puoi dichiarare l'intera classe come metodi finali o specifici che non vuoi essere cambiato.

Poiché la dichiarazione di una classe non impedisce di creare un'istanza di una classe, ciò non significa che impedirà alla classe di avere le caratteristiche di un oggetto. È solo che dovrai attenersi ai metodi nel modo in cui sono dichiarati nella classe.


1

pensa a FINAL come alla "fine della linea" - quel ragazzo non può più produrre prole. Quindi, quando lo vedi in questo modo, ci sono tonnellate di scenari del mondo reale che ti imbatterai che richiedono di contrassegnare un indicatore di "fine linea" per la classe. È Domain Driven Design - se il tuo dominio richiede che una determinata ENTITY (classe) non possa creare sottoclassi, quindi contrassegnalo come FINALE.

Dovrei notare che non c'è niente che ti impedisce di ereditare una classe "dovrebbe essere taggata come finale". Ma questo è generalmente classificato come "abuso dell'ereditarietà" e fatto perché molto spesso vorresti ereditare alcune funzioni dalla classe base nella tua classe.

L'approccio migliore è guardare il dominio e lasciarlo dettare le tue decisioni di progettazione.


1

Come detto in precedenza, se si desidera che nessuno possa modificare la funzionalità del metodo, è possibile dichiararlo come definitivo.

Esempio: percorso del file del server delle applicazioni per download / upload, suddivisione della stringa in base all'offset, tali metodi è possibile dichiararlo Final in modo che tali funzioni del metodo non vengano modificate. E se desideri tali metodi finali in una classe separata, allora definisci quella classe come Classe finale. Quindi la classe Final avrà tutti i metodi finali, dove come metodo Final può essere dichiarato e definito in una classe non finale.



1

Diciamo che hai una Employeeclasse che ha un metodo greet. Quando greetviene chiamato il metodo, viene semplicemente stampato Hello everyone!. Questo è il comportamento atteso del greetmetodo

public class Employee {

    void greet() {
        System.out.println("Hello everyone!");
    }
}

Ora, lascia la GrumpyEmployeesottoclasse Employeee sostituisci il greetmetodo come mostrato di seguito.

public class GrumpyEmployee extends Employee {

    @Override
    void greet() {
        System.out.println("Get lost!");
    }
}

Ora nel codice qui sotto dai un'occhiata al sayHellometodo. Prende l' Employeeistanza come parametro e chiama il metodo greet sperando che direbbe Hello everyone!Ma quello che otteniamo è Get lost!. Questo cambiamento nel comportamento è dovuto aEmployee grumpyEmployee = new GrumpyEmployee();

public class TestFinal {
    static Employee grumpyEmployee = new GrumpyEmployee();

    public static void main(String[] args) {
        TestFinal testFinal = new TestFinal();
        testFinal.sayHello(grumpyEmployee);
    }

    private void sayHello(Employee employee) {
        employee.greet(); //Here you would expect a warm greeting, but what you get is "Get lost!"
    }
}

Questa situazione può essere evitata se la Employeeclasse è stata creata final. Immagina solo la quantità di caos che un programmatore sfacciato potrebbe causare se StringClass non fosse dichiarata come final.


1

La classe finale non può essere estesa ulteriormente. Se non abbiamo bisogno di rendere ereditabile una classe in Java, possiamo usare questo approccio.

Se dobbiamo solo fare in modo che determinati metodi in una classe non vengano sovrascritti, possiamo solo mettere la parola chiave finale davanti a loro. Lì la classe è ancora ereditabile.


-1

L'orientamento agli oggetti non riguarda l'ereditarietà, riguarda l'incapsulamento. E l'eredità rompe l'incapsulamento.

Dichiarare un finale di classe ha perfettamente senso in molti casi. Qualsiasi oggetto che rappresenti un "valore" come un colore o una somma di denaro potrebbe essere definitivo. Stanno da soli.

Se stai scrivendo librerie, rendi le tue lezioni definitive a meno che non le indentri esplicitamente come derivate. Altrimenti, le persone potrebbero derivare le tue classi e sovrascrivere i metodi, rompendo i tuoi presupposti / invarianti. Ciò può avere anche implicazioni per la sicurezza.

Joshua Bloch in "Effective Java" raccomanda di progettare esplicitamente l'ereditarietà o di vietarlo e osserva che progettare per l'ereditarietà non è così facile.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.