Quali sono i buoni motivi per vietare l'ereditarietà in Java, ad esempio utilizzando le classi finali o le classi utilizzando un unico costruttore privato senza parametri? Quali sono i buoni motivi per rendere definitivo un metodo?
Quali sono i buoni motivi per vietare l'ereditarietà in Java, ad esempio utilizzando le classi finali o le classi utilizzando un unico costruttore privato senza parametri? Quali sono i buoni motivi per rendere definitivo un metodo?
Risposte:
Il tuo miglior riferimento qui è l'articolo 19 dell'eccellente libro di Joshua Bloch "Effective Java", chiamato "Progettazione e documento per ereditarietà o vietarlo". (È l'articolo 17 nella seconda edizione e l'articolo 15 nella prima edizione.) Dovresti davvero leggerlo, ma riassumerò.
L'interazione delle classi ereditate con i loro genitori può essere sorprendente e imprevedibile se l'antenato non è stato progettato per essere ereditato.
Le classi dovrebbero quindi venire in due tipi:
Classi progettate per essere estese e con sufficiente documentazione per descrivere come dovrebbe essere fatto
Classi contrassegnate come finali
Se stai scrivendo un codice puramente interno, questo potrebbe essere un po 'eccessivo. Tuttavia, lo sforzo aggiuntivo necessario per aggiungere cinque caratteri a un file di classe è molto ridotto. Se stai scrivendo solo per un consumo interno, un programmatore futuro può sempre rimuovere il "finale" - puoi pensarlo come un avvertimento che dice "questa classe non è stata progettata pensando all'eredità".
È possibile che si desideri rendere definitivo un metodo in modo che le classi di esclusione non possano modificare il comportamento su cui si fa affidamento in altri metodi. I metodi chiamati nei costruttori sono spesso dichiarati finali, quindi non si ottengono spiacevoli sorprese durante la creazione di oggetti.
final
.
Un motivo per rendere finale una classe sarebbe se volevi forzare la composizione sull'eredità. Ciò è generalmente desiderabile per evitare l'accoppiamento stretto tra le classi.
Ci sono 3 casi d'uso in cui puoi scegliere i metodi finali.
Scopo del finale di classe:
In modo che nessun corpo possa estendere quelle classi e cambiare il loro comportamento.
Ad esempio: Wrapper class Integer è una classe finale. Se quella classe non è definitiva, chiunque può estendere Integer nella propria classe e modificare il comportamento di base della classe intera. Per evitare ciò, java ha creato tutte le classi wrapper come classi finali.
DerivedInteger
, non cambia ancora la classe Integer originale e chiunque la usi lo DerivedInteger
fa a proprio rischio, quindi non capisco perché sia un problema.
potresti voler creare oggetti immutabili ( http://en.wikipedia.org/wiki/Immutable_object ), potresti creare un singleton ( http://en.wikipedia.org/wiki/Singleton_pattern ), oppure potresti voler per impedire a qualcuno di ignorare il metodo per motivi di efficienza, sicurezza o protezione.
L'ereditarietà è come una motosega: molto potente, ma terribile nelle mani sbagliate. O progetti una classe da cui ereditare (che può limitare la flessibilità e impiegare molto più tempo) o dovresti proibirla.
Vedi gli articoli 16 e 17 della seconda edizione di Java effettivi o il mio post sul blog "Imposta sulle successioni" .
Hmmm ... posso pensare a due cose:
Potresti avere una classe che si occupa di determinati problemi di sicurezza. Sottoclassandolo e alimentando il sistema con la sua sottoclasse, un utente malintenzionato può eludere le restrizioni di sicurezza. Ad esempio, la tua applicazione potrebbe supportare plug-in e se un plug-in può semplicemente sottoclassare le tue classi rilevanti per la sicurezza, può usare questo trucco per far entrare in qualche modo una sua sottoclasse. Tuttavia, questo è piuttosto qualcosa che Sun deve affrontare per quanto riguarda applet e simili, forse non un caso così realistico.
Uno molto più realistico è evitare che un oggetto diventi mutevole. Ad esempio, poiché le stringhe sono immutabili, il codice può conservare in modo sicuro riferimenti ad esso
String blah = someOtherString;
invece di copiare prima la stringa. Tuttavia, se puoi sottoclassare String, puoi aggiungere dei metodi che consentono di modificare il valore della stringa, ora nessun codice può fare più affidamento sul fatto che la stringa rimarrà la stessa se copia la stringa come sopra, invece deve duplicare il corda.
Inoltre, se stai scrivendo una classe commerciale a codice chiuso, potresti non voler che le persone siano in grado di cambiare la funzionalità in linea, specialmente se hai bisogno di fornirti supporto e le persone hanno scavalcato il tuo metodo e ti lamenti del fatto che chiamarlo risultati inaspettati.
Se contrassegni le classi e i metodi come finali, potresti notare un piccolo guadagno in termini di prestazioni, poiché il runtime non deve cercare il metodo di classe giusto per invocare un determinato oggetto. I metodi non finali sono contrassegnati come virtuali in modo che possano essere correttamente estesi se necessario, i metodi finali possono essere direttamente collegati o compilati in linea nella classe.
Per impedire alle persone di fare cose che potrebbero confondere se stesse e gli altri. Immagina una libreria di fisica in cui hai alcune costanti o calcoli definiti. Senza usare la parola chiave finale, qualcuno potrebbe venire e ridefinire i calcoli di base o le costanti che non dovrebbero MAI cambiare.
Volete rendere definitivo un metodo in modo che le classi di override non cambino il suo comportamento. Quando vuoi essere in grado di cambiare il comportamento, rendi pubblico il metodo. Quando si esegue l'override di un metodo pubblico, è possibile modificarlo.