Ripetizione di un’eccezione: perché il metodo viene compilato senza una clausola di lancio?

Nel codice sorgente riportato di seguito sto rilanciando un’eccezione.
Perché non è necessario inserire la parola chiave throws nella firma del metodo?

 public void throwsOrNotThrowsThatsTheQuestion() { try { // Any processing } catch (Exception e) { throw e; } } 

Questo comportamento sembra verificarsi solo su Java 1.7. Durante la compilazione con 1.6, ottengo il seguente messaggio di errore del compilatore:

 c:\dev\src\misc>javac -source 1.6 Main.java warning: [options] bootstrap class path not set in conjunction with -source 1.6 Main.java:22: error: unreported exception Exception; must be caught or declared to be thrown throw e; ^ 1 error 1 warning 

Ma con Java 1.7, compila.

 c:\dev\src\misc>javac -source 1.7 Main.java c:\dev\src\misc> 

… Fino a quando non lancio effettivamente Exception nel blocco try :

 public static void throwsOrNotThrowsThatsTheQuestion() { try { // Any processing throw new IOException("Fake!"); } catch (Exception e) { throw e; } 

Compilazione …

 c:\dev\src\misc>javac -source 1.7 Main.java Main.java:22: error: unreported exception IOException; must be caught or declare d to be thrown throw e; ^ 1 error 

Sembra che Java 1.7 sia abbastanza intelligente da rilevare il tipo di Exception che potrebbero essere generate analizzando il codice di blocco try , dove 1.6 ha appena visto throw e; di tipo Exception e ha dato un errore solo per quello.

Cambiarlo per lanciare una RuntimeException lo ha fatto compilare come previsto, perché come sempre, le Exception non selezionate non hanno bisogno di una clausola throws :

 public static void throwsOrNotThrowsThatsTheQuestion() { try { // Any processing throw new RuntimeException("Fake!"); } catch (Exception e) { throw e; } 

Compilazione …

 c:\dev\src\misc>javac -source 1.7 Main.java c:\dev\src\misc> 

La spiegazione

Ecco cosa sta succedendo:

Java 7 ha introdotto un controllo di tipo più inclusivo . Citando …

Considera il seguente esempio:

 static class FirstException extends Exception { } static class SecondException extends Exception { } public void rethrowException(String exceptionName) throws Exception { try { if (exceptionName.equals("First")) { throw new FirstException(); } else { throw new SecondException(); } } catch (Exception e) { throw e; } } 

Il blocco try di questo esempio può lanciare FirstException o SecondException. Si supponga di voler specificare questi tipi di eccezione nella clausola throws della dichiarazione del metodo rethrowException. Nelle versioni precedenti a Java SE 7, non è ansible farlo. Poiché il parametro di eccezione della clausola catch, e, è di tipo Exception, e il blocco catch rilancia il parametro di eccezione e, è ansible specificare solo il tipo di eccezione Exception nella clausola throws della dichiarazione del metodo rethrowException.

Tuttavia, in Java SE 7, è ansible specificare i tipi di eccezione FirstException e SecondException nella clausola throws nella dichiarazione del metodo rethrowException . Il compilatore Java SE 7 può determinare che l’eccezione generata dal lancio dell’istruzione deve provenire dal blocco try e le uniche eccezioni generate dal blocco try possono essere FirstException e SecondException. Anche se il parametro di eccezione della clausola catch, e, è di tipo Exception, il compilatore può determinare che si tratta di un’istanza di FirstException o SecondException:

(sottolineatura mia)

 public void rethrowException(String exceptionName) throws FirstException, SecondException { try { // ... } catch (Exception e) { throw e; } } 

java.lang.Exception è un’eccezione controllata, quindi non funzionerà né compila. Funzionerebbe con un unckeched (java.lang.RuntimeException). Non fa assolutamente differenza se si lancia un’eccezione all’interno di un blocco catch o meno.

L’errore del compilatore sarebbe simile a questo (a seconda del compilatore):

java: eccezione non segnalata java.lang.Exception; deve essere catturato o dichiarato essere gettato

EDIT: Java 7 può gestire situazioni del genere se non si lancia mai l’eccezione

Se si verifica un’eccezione controllata, è necessario averla nella lista dei tiri

 public void retrhowChecked() throws Exception { try { throw new IOException(); } catch(Exception e) { throw e; } } 

Se si lancia un’eccezione non controllata non è necessario inserirlo nell’elenco dei lanci, è ansible utilizzarla per impacchettare un’eccezione controllata all’interno di una casella deselezionata per evitare di rompere il codice che utilizza questo metodo se si modifica il metodo in questione in tale in modo che dopo la modifica possa produrre un’eccezione controllata. Ma bisogna stare attenti, l’Eccezione è lì per essere gestita!

 public void retrhowUnchecked() { try { throw new IOException(); } catch(Exception e) { throw new RuntimeException(e); } } 

Maggiori informazioni su Eccezioni qui .

Perché non è necessario inserire la parola chiave throws nella firma del metodo?

Puoi mettere quella causa al tuo // Any processing non genera alcuna eccezione controllata .

Esempio:

Questo compila bene.

 public void throwsOrNotThrowsThatsTheQuestion() { try { throw new RuntimeException(); } catch (Exception e) { throw e; } 

Questo non verrà compilato, è necessario aggiungere la clausola di throws .

 public void throwsOrNotThrowsThatsTheQuestion() { try { throw new Exception(); } catch (Exception e) { //do something like log and rethrow throw e; } } 

Funziona da quando java 7. Nella versione precedente viene generata un’eccezione. Maggiori informazioni su rethrow in java 7

Quando usi i lanci con un metodo, significa che l’istruzione che chiamerà quel metodo deve essere circondata da un blocco catch try.

Ma se il metodo include già il blocco catch try, non è necessaria alcuna dichiarazione di thorws poiché l’eccezione generata dal metodo viene gestita solo lì.

L’istruzione che chiama questo metodo non ha bisogno di essere circondata da try catch block.

Spero che questo chiarisca il tuo dubbio.

lanciare una nuova Exception(); è qualcosa che non dovresti mai fare in un blocco catch, ma potresti dover o volere throw new SomeException(throwable); (preservando l’intera traccia dello stack) anziché lanciare il throwable; per conformarsi all’API del tuo metodo, ad esempio quando dichiara di lanciare SomeException ma stai chiamando il codice che potrebbe generare una IOException che non vuoi aggiungere alla clausola di lancio del metodo.

Il caso più probabile è la nuova RuntimeException (lanciabile); per evitare di avere una clausola di tiri del tutto.