Catturare un'eccezione e riproporla, ma non è un'eccezione


10

Mi sono imbattuto in codice simile a questo:

void run() {
    try {
        doSomething();
    } catch (Exception ex) {
        System.out.println("Error: " + ex);
        throw ex;
    }
}

void doSomething() {
    throw new RuntimeException();
}

Questo codice mi sorprende perché sembra che il run()metodo sia in grado di lanciare un Exception, dal momento che lo cattura Exceptione lo ricodifica, ma il metodo non è dichiarato da lanciare Exceptione apparentemente non è necessario. Questo codice viene compilato correttamente (almeno in Java 11).

La mia aspettativa sarebbe che avrei dovuto dichiarare throws Exceptionnel run()metodo.

Ulteriori informazioni

In modo simile, se doSomethingviene dichiarato di buttare IOExceptionallora solo IOExceptionbisogno di essere dichiarato nel run()-Metodo, anche se Exceptionviene catturato e rilanciati.

void run() throws IOException {
    try {
        doSomething();
    } catch (Exception ex) {
        System.out.println("Error: " + ex);
        throw ex;
    }
}

void doSomething() throws IOException {
    // ... whatever code you may want ...
}

Domanda

Java di solito ama la chiarezza, qual è la ragione di questo comportamento? È sempre stato così? Cosa nelle specifiche del linguaggio Java consente al run()metodo di non dover dichiarare throws Exceptionnei frammenti di codice sopra? (Se lo aggiungessi, IntelliJ mi avverte che Exceptionnon viene mai lanciato).


3
Interessante. Quale compilatore stai usando? Se è un compilatore IDE, verifica con javac: mi sono imbattuto in casi in cui il compilatore Eclipse era più indulgente.
M. Prokhorov,

2
Posso riprodurre questo comportamento su openjdk-8. In particolare la compilazione con il -source 1.6flag genera un errore di compilazione come previsto. La compilazione con compatibilità sorgente 7 non non aumentare l'errore di compilazione
Vogel612

1
sembra che il compilatore sia più intelligente da Java 7 e fa più controlli sull'eccezione effettiva che potrebbe essere generata.
michalk,

2
Questa domanda non è un duplicato e la risposta può essere trovata nel link che ho fornitoIn detail, in Java SE 7 and later, when you declare one or more exception types in a catch clause, and rethrow the exception handled by this catch block, the compiler verifies that the type of the rethrown exception meets the following conditions : 1. 1. The try block is able to throw it. 2. There are no other preceding catch blocks that can handle it. 3. It is a subtype or supertype of one of the catch clause's exception parameters.
michalk

2
Il duplicato attualmente contrassegnato è sicuramente rilevante, ma non fornisce una risposta sufficientemente dettagliata IMO. C'è un link al JLS nei commenti alla risposta lì, oltre a quello nessuna informazione.
Simon Forsberg,

Risposte:


0

Non ho esaminato JLScome hai fatto nella tua domanda, quindi per favore prendi questa risposta con un granello di sale. Volevo fare un commento, ma sarebbe stato troppo grande.


Trovo divertente a volte, come javacè piuttosto "intelligente" in alcuni casi (come nel tuo caso), ma lascia molte altre cose da gestire in seguito JIT. In questo caso, è solo che il compilatore "può dire" che solo un RuntimeExceptionverrebbe catturato. Questo è ovvio, è l'unica cosa da buttare dentro doSomething. Se modifichi leggermente il codice in:

void run() {
    try {
        doSomething();
    } catch (Exception ex) {
        Exception ex2 = new Exception();
        System.out.println("Error: " + ex);
        throw ex2;
    }
}

vedrai un comportamento diverso, perché ora javacpuoi dire che c'è un nuovo Exceptionche stai lanciando, non correlato a quello che hai catturato.

Ma le cose sono tutt'altro che ideali, puoi "ingannare" nuovamente il compilatore tramite:

void run() {
    try {
        doSomething();
    } catch (Exception ex) {
        Exception ex2 = new Exception();
        ex2 = ex;
        System.out.println("Error: " + ex);
        throw ex2;
    }
}

IMO, a causa ex2 = ex;sua non dovrebbe fallire di nuovo, ma lo fa.

Nel caso in cui questo è stato compilato javac 13+33


Ho letto in alcuni link che qualcuno ha fornito che se si riassegna l'eccezione rilevata nel blocco catch, il compilatore non è in grado di essere intelligente. Presumo che qualcosa di simile si applichi in questo caso. Il compilatore sa che ex2verrà generata l' eccezione, è stata originariamente creata come Exceptionma viene quindi riassegnata a ex, e quindi il compilatore non è in grado di essere intelligente.
Simon Forsberg,

@SimonForsberg qualcuno che ha una passione per JLSpotrebbe venire e ha fornito le citazioni necessarie per dimostrarlo; purtroppo non li ho.
Eugene,

Per la cronaca, quando cambio il blocco catch per contenere una riassegnazione dell'eccezione catturata a se stesso ( ex = ex;), l'euristica non viene più applicata. Questo comportamento sembra essere applicato a tutti i livelli sorgente da 7 a 11 e probabilmente 13
Vogel612

Dai un'occhiata a questa domanda che è anche un duplicato. Questo e il duplicato del possibile duplicatore lo spiegano e si collegano anche a JLS.
Michalk,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.