Nella mia esperienza, c'è una e una sola ragione per ignorare Object.finalize()
, ma è un'ottima ragione :
Inserire il codice di registrazione degli errori in finalize()
cui avvisare se si dimentica di invocare close()
.
L'analisi statica può solo rilevare omissioni in scenari di utilizzo banali e gli avvisi del compilatore menzionati in un'altra risposta hanno una visione così semplicistica delle cose che devi effettivamente disabilitarle per ottenere qualcosa di non banale. (Ho molti più avvisi abilitati rispetto a qualsiasi altro programmatore di cui conosco o di cui non ho mai sentito parlare, ma non ho abilitati avvisi stupidi.)
La finalizzazione potrebbe sembrare un buon meccanismo per assicurarsi che le risorse non vadano fuori strada, ma la maggior parte delle persone la vede in un modo completamente sbagliato: la considera come un meccanismo alternativo di fallback, una "seconda possibilità" che salverà automaticamente la giorno smaltendo le risorse che hanno dimenticato. Questo è completamente sbagliato . Ci deve essere un solo modo di fare qualsiasi cosa: o chiudi sempre tutto o la finalizzazione chiude sempre tutto. Ma poiché la finalizzazione non è affidabile, la finalizzazione non può esserlo.
Quindi, esiste questo schema che io chiamo smaltimento obbligatorio e stabilisce che il programmatore è responsabile della chiusura esplicita di tutto ciò che implementa Closeable
o AutoCloseable
. (L'istruzione try-with-resources conta ancora come chiusura esplicita.) Naturalmente, il programmatore potrebbe dimenticare, quindi è qui che entra in gioco la finalizzazione, ma non come una fata magica che magicamente renderà le cose giuste alla fine: se la finalizzazione scopre che close()
non è stato invocato, non lo ètenta di invocarlo, proprio perché ci saranno (con certezza matematica) orde di programmatori n00b che faranno affidamento su di esso per fare il lavoro che erano troppo pigri o troppo assenti per farlo. Quindi, con lo smaltimento obbligatorio, quando la finalizzazione scopre che close()
non è stata invocata, registra un messaggio di errore rosso brillante, che dice al programmatore con grandi e grosse lettere maiuscole per sistemare la sua roba, la sua roba.
Come ulteriore vantaggio, si dice che "la JVM ignorerà un banale metodo finalize () (ad esempio uno che ritorna semplicemente senza fare nulla, come quello definito nella classe Object)", quindi con lo smaltimento obbligatorio è possibile evitare qualsiasi finalizzazione overhead in tutto il tuo sistema ( vedi la risposta di alip per informazioni su quanto sia terribile questo overhead) codificando il tuo finalize()
metodo in questo modo:
@Override
protected void finalize() throws Throwable
{
if( Global.DEBUG && !closed )
{
Log.Error( "FORGOT TO CLOSE THIS!" );
}
//super.finalize(); see alip's comment on why this should not be invoked.
}
L'idea alla base di ciò è che Global.DEBUG
è una static final
variabile il cui valore è noto al momento della compilazione, quindi se è false
così il compilatore non emetterà alcun codice per l'intera if
istruzione, il che renderà questo un finalizzatore banale (vuoto), che a sua volta significa che la tua classe verrà trattata come se non avesse un finalizzatore. (In C # questo sarebbe fatto con un bel #if DEBUG
blocco, ma cosa possiamo fare, questo è Java, dove paghiamo un'apparente semplicità nel codice con un sovraccarico aggiuntivo nel cervello.)
Maggiori informazioni sullo smaltimento obbligatorio, con ulteriori discussioni sullo smaltimento delle risorse in dot Net, qui: michael.gr: smaltimento obbligatorio rispetto all'abominio "Smaltimento-smaltimento"
finalize()
è un po 'incasinata. Se mai lo si implementa, assicurarsi che sia thread-safe rispetto a tutti gli altri metodi sullo stesso oggetto.