Comprendo la motivazione per la creazione di singoli metodi di una classe sealed
/final
, ma quale scopo impedisce completamente l'ereditarietà dalla classe? Anche se consentire l'override di determinati metodi può causare che cose brutte accadano, non riesco a capire come l'ereditarietà della classe possa semplicemente aggiungervi un comportamento senza sovrascrivere il comportamento esistente potrebbe essere una brutta cosa. Se vuoi veramente disabilitare l'override di qualsiasi cosa nella tua classe, perché non basta rendere definitivo ogni metodo, ma permetti comunque l'ereditarietà allo scopo di aggiungere un comportamento?Perché dovresti rendere un'intera classe sigillata/finale?
risposta
Un team di sviluppo di organizzazioni/software potrebbe voler applicare determinati standard di codifica. Ad esempio, per migliorare la leggibilità, potrebbero volere solo l'attributo X della classe Y da modificare con il metodo Z e nient'altro. Se la classe non fosse definitiva, alcuni sviluppatori potrebbero estendere la classe Y e aggiungere il metodo W, che potrebbe modificare X in un modo diverso e causare confusione e ritardo quando si tratta di comprendere il codice scritto.
Non sta rendendo l'attributo X privato la stessa cosa? – Dalius
@Dalius Anche con X privato, forse è desiderabile che solo una chiamata diretta a Z su un oggetto Y abbia effetto su X. Se una classe W ha la precedenza su Z, allora potresti essere in grado di modificare X attraverso una chiamata a Z su un oggetto W, che rompe il desiderio iniziale di cambiare X solo attraverso una chiamata al metodo Z direttamente da un oggetto Y. Personalmente non ho mai visto una situazione del genere, ma immagino che possa succedere. –
Ci sono alcune buone discussioni su questo nel "Perchè String è definitiva in Java" domanda: Why is String class declared final in Java?
La sintesi di base in questo caso è, String è immutabile, e se potesse essere sottoclasse, la sottoclasse potrebbe renderlo mutabile.
Pensateci in questo modo: avete passato gli ultimi 3 mesi a lavorare su una classe base per il vostro framework che altri useranno, e l'intera coerenza del framework si basa sul codice scritto in questa classe, modificando questa classe può causare bug, errori e comportamenti scorretti del tuo framework, come puoi impedire ai tuoi grandi programmatori di estenderlo e sovrascrivere uno dei suoi metodi?
Oltre a questo ci sono classi che dovrebbero essere immutabili, come la classe java.lang.String in Java, estenderla e cambiarne il comportamento potrebbe creare un gran casino!
Infine, talvolta, impostare una classe o un metodo come finale aumenterà le prestazioni, ma non l'ho mai testato, e non vedo alcun motivo sulla terra per rendere una classe/metodo finale per un guadagno di prestazioni molto piccolo che è uguale a zero rispetto al tempo necessario per fare altre cose nel programma.
1. La domanda riguarda consentire alla sottoclasse di aggiungere un comportamento. Capisco perché vorresti vietare l'override. 2. Se un corso ti richiede 3 mesi per scrivere, probabilmente è troppo. – dsimcha
Non prendete letteralmente 3 mesi, l'idea era una classe importante, in un contesto fragile, e dove ogni altra classe si aspetta che obbedirà a un determinato comportamento. –
Più in generale, una classe può essere resa definitiva per preservare gli invarianti che lo fanno funzionare. Consiglio vivamente "Effective Java" di Joshua Bloch per un'eccellente discussione su questo e sui problemi correlati.
Penso che dipenda.
Se si dispone di un'applicazione interna, non utilizzerò il livello finale di classe per impostazione predefinita. Il codice diventa troppo confuso. In alcune circostanze, il modificatore finale è addirittura davvero fastidioso, se voglio refactoring in modo sicuro (vedi il termine programming by difference). In alcuni casi la 'default-final-mania' ha creato grossi problemi quando ho provato a refactoring.
D'altro canto, se progetto un api/framework in cui il codice è più basato sull'estensione, utilizzerei final per le classi "in modo più aggressivo". Qui l'estensione errata può essere un grosso problema e il modificatore 'finale' è un modo sicuro per evitare tali errori di programmazione.
Ma io sono contrario a dire di usare "finale" come impostazione predefinita. Ha spesso più svantaggi come vantaggi. Le impostazioni predefinite dovrebbero essere "impostazioni sensate e più comuni".
Per quanto ne so, si tratta di mantenere invarianti.
In una situazione analoga, ad esempio, è possibile rendere pubblici i propri attributi, ma in tal caso si perderebbe il controllo sui propri valori perché i client sarebbero in grado di modificarli direttamente, interrompendo alcune proprietà che si desidera mantenere. Quindi, allo stesso modo in cui proibisci ai client di accedere ad alcuni campi (la maggior parte effettivamente) direttamente per mantenere invariati, puoi anche rendere definitiva la tua classe/metodo per mantenerne invariata.
Un esempio comune che vedo ha a che fare con l'immutabilità, che può essere interrotta se una classe non è definitiva.
Il problema non è proprio quando un utente estende una classe e la usa solo nel suo sistema, vivendo con il suo "disordine" isolato da tutto il resto. Il vero problema riguarda il fatto che classi/metodi/attributi/oggetti/... non esistono in modo isolato.
Supponiamo che tu abbia una collezione di classi CC1 che collabora a un obiettivo e una di quelle classi CC1_A dovrebbe "produrre" solo oggetti immutabili (o qualsiasi altra invariante che desideri). Quindi vai avanti ed estendi CC1_A, rendendo CC2_AX (esteso A in una raccolta di seconda classe). Quello che puoi fare ora è usare roba CC1 passando un CC2_AX dove è richiesto un CC1_A. Tuttavia, CC1 è stato creato ipotizzando che gli oggetti CC1_A siano immutabili, ma che gli oggetti CC2_AX non lo siano (si supponga che non siano solo per l'esempio). Ciò può causare seri problemi al tuo programma. Tale problema può accadere anche internamente in una classe. Ad esempio, i metodi di CC1_A probabilmente supporteranno che gli oggetti CC1_A siano immutabili, quindi i metodi di CC2_AX che provengono da CC1_A possono potenzialmente fallire perché CC2_AX interrompe la proprietà di immutabilità.
Nei nostri sistemi, oggi, abbiamo molte classi e molto di quello che facciamo è implicito. Quindi non pensiamo sempre alle invarianti che vorremmo mantenere. Questo non rende le cose più facili.
Tra l'altro, alcune altre invarianti includono cose come:
- Il metodo di ordinamento deve essere stabile.
- Il metodo come > utilizza l'algoritmo <altro>.
- Questa classe < > deve utilizzare l'implementazione di liste semplicemente collegate come descritto in < in qualche luogo >.
Si va avanti e avanti. Alcune classi sono utili solo perché mantengono invarianti più forti (ad esempio, l'uso di un particolare algoritmo) e l'estensione consente alle persone di rompere facilmente quel genere di cose. E una ragione per cui questo è tutto un problema ha a che fare con queste cose che non vivono in isolamento.
Nel caso di java.util.UUID trovo l'impossibilità di sottoclasse frustrante. –