Infine blocca quando non vengono lanciate eccezioni

-1

Sto facendo alcune operazioni di pulizia che potrebbero generare un'eccezione e voglio implementare la logica di un blocco finally , ma solo se non vengono lanciate eccezioni:

L'implementazione iniziale è questa:

acquireResource();
try {
    // stuff
}
catch (Exception e) {
    cleanupFromException(e);
    throw e;
}
finally {
    cleanupTidy();
}

Tuttavia, se viene lanciata un'eccezione, chiamerà cleanupFromException , quindi cleanupTidy (che è ciò che non voglio).

Quindi la mia prossima implementazione è questa:

acquireResource();
try {
    // stuff
    cleanupTidy();
}
catch (Exception e) {
    cleanupFromException(e);
    throw e;
}

Tuttavia, questo non chiamerà cleanupTidy se il codice nel blocco try esegue salti - return , continue , break , ecc - all'esterno del blocco try.

Il meglio che riesco a trovare è questo:

acquireResource();
boolean exceptionThrown = false;
try {
    // stuff
}
catch (Exception e) {
    exceptionThrown = true;
    cleanupFromException(e);
    throw e;
}
finally {
    if (!exceptionThrown)
        cleanupTidy();
}

Ma questo ha un odore un po 'sdolcinato, ed è un sacco di piatti. C'è un modo migliore di avere un finally che viene eseguito solo se non viene lanciata un'eccezione (in Java)? O un modo di avvolgerlo in un'API più semplice?

    
posta thecoop 04.07.2016 - 13:20
fonte

5 risposte

2

La soluzione semplice qui è di ristrutturare il tuo codice. Piuttosto che:

acquireResource();
try {
    // stuff
    cleanupTidy();
}
catch (Exception e) {
    cleanupFromException(e);
    throw e;
}

Sposta stuff in un altro metodo:

acquireResource();
try {
     doStuff();
     cleanupTidy();
}
catch (Exception e) {
    cleanupFromException(e);
    throw e;
}

In questo modo, il contenuto di doStuff può break , continue o return tanto quanto piace, e cleanupTidy() sarà sempre chiamato a meno che non si verifichi un'eccezione.

Inoltre, sconsiglio di prendere Exception . Dovresti essere più specifico su quali eccezioni catturi.

    
risposta data 05.07.2016 - 10:07
fonte
1

Hai ragione a essere preoccupato. Il tuo codice implica che c'è qualcosa su un'eccezione che non è stata lanciata che ti fa venir voglia di chiamare cleanupTidy() . Non è vero, ma qualcuno che legge il codice in futuro (anche se leggi il codice tra 20 anni) potrebbe pensare che lo sia.

Il seguente codice viene eseguito in modo identico al tuo, ma non dice bugie:

acquireResource();
boolean cleanupNeeded = true;
try {
    // stuff
}
catch (Exception e) {
    cleanupNeeded=false;
    cleanupFromException(e);
    throw e;
}
finally {
    if (cleanupNeeded)
      cleanupTidy();
}

Se ci fosse un luogo in cui memorizzare il flag cleanupNeeded all'interno di qualcosa che era visibile a cleanupFromException e cleanupTidy , allora sarei propenso a mettere tutta la cleanupNeeded di gestione all'interno di quelle funzioni. Anzi, andrei oltre, dal momento che cleanupNeeded è solo una scorciatoia per "esistono cose che hanno bisogno di essere ripulite". Prenderò in considerazione ognuna di queste cose e troverò un modo per decidere nella funzione di pulizia se questa cosa deve essere ripulita. Puoi implicitamente doverlo fare comunque, dal momento che dipendendo da quanto "roba" hai, ci saranno cose diverse che non hanno bisogno di essere ripulite.

    
risposta data 04.07.2016 - 13:31
fonte
1

I want to implement the logic of a finally block, but only if no exceptions are thrown

Un blocco di codice che viene eseguito se non vengono lanciate eccezioni?

Questo è solo un blocco di codice dopo tutto ciò che potrebbe generare un'eccezione.

However, this will not call cleanupTidy if the code in the try block does any jumps - return, continue, break, etc - to the outside of the try block.

Quindi hai bisogno di un blocco "finally" per eseguire dopo l'elaborazione "normale", ma prima qualsiasi gestione delle eccezioni? Sembra che tu abbia bisogno di un nested try.. finally block:

acquireResource();
try {
    try {
        // stuff
    } 
    finally { 
        cleanupTidy();
    }
}
catch (Exception e) {
    cleanupFromException(e);
    throw e;
}

Inoltre; perché stai rilevando Eccezione ?

Per me, prendere la classe base fondamentale per tutti Tipi di eccezioni genera una [grande] bandiera rossa. Puoi essere più specifico nel [tipo di] Eccezione che stai catturando? Il fatto che tu passi l'Eccezione come argomento per una routine di "pulizia" mi fa pensare che la routine di pulizia stia probabilmente facendo troppo e sarà troppo lontana dalla scena del crimine. Potresti stare meglio con il codice di pulizia specifico per ogni tipo di eccezione che intercetti:

try { 
    // stuff 
} 
catch( IOException e ) { 
    // I/O cleanup 
}
catch( NullReferenceException e ) { 
    // Null reference cleanup 
} 
catch( Exception e ) {
    // Any other business 
} 
    
risposta data 04.07.2016 - 13:38
fonte
1

Ho trovato una soluzione (alquanto icky) con meno lastre. Stavo passando la mia eccezione per aggiungere eventuali eccezioni generate durante la chiusura alle eccezioni soppresse dell'eccezione originale. Tuttavia, try-with-resources fa questo per me:

acquireResource();
try (Closeable c = this::cleanup) {
    // stuff
}

Questo non è un buon uso di try-with-resources, ma risolve il mio problema: la chiusura viene chiamata una sola volta e qualsiasi eccezione generata verrà propagata correttamente, senza eccezioni.

    
risposta data 04.07.2016 - 13:52
fonte
0

Un blocco "finally" contiene quello che viene eseguito, non importa quale. Soprattutto è destinato ad essere eseguito se viene lanciata un'eccezione. Se si inserisce il codice in un blocco finally e quindi si impedisce l'esecuzione, ad esempio impostando cleanupNeeded = false, non lo si utilizza come previsto. Il codice sopra potrebbe essere cambiato in

acquireResource();
try {
    // stuff
}
catch (Exception e) {
    cleanupFromException(e);
    throw e;
}

cleanupTidy();

L'OP fondamentalmente diceva: sto usando "finalmente", ma non fa quello che voglio. La risposta semplice è: non usare "finalmente".

    
risposta data 05.07.2016 - 12:02
fonte

Leggi altre domande sui tag