Saya menemukan kode yang terlihat seperti ini:
void run() {
try {
doSomething();
} catch (Exception ex) {
System.out.println("Error: " + ex);
throw ex;
}
}
void doSomething() {
throw new RuntimeException();
}
Kode ini mengejutkan saya karena kelihatannya run()
-metode ini mampu melempar Exception
, karena menangkap Exception
dan kemudian memunculkannya kembali, tetapi metode ini tidak dinyatakan untuk melempar Exception
dan tampaknya tidak perlu. Kode ini mengkompilasi dengan baik (setidaknya dalam Java 11).
Harapan saya adalah bahwa saya harus mendeklarasikan throws Exception
dalam run()
-metode.
Informasi tambahan
Dengan cara yang sama, jika doSomething
dideklarasikan untuk melempar IOException
maka hanya IOException
perlu dideklarasikan dalam run()
-metode, meskipun Exception
ditangkap dan dipulihkan.
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 ...
}
Pertanyaan
Java biasanya suka kejelasan, apa alasan di balik perilaku ini? Apakah selalu seperti ini? Apa dalam Spesifikasi Bahasa Jawa yang memungkinkan run()
metode tidak perlu mendeklarasikan throws Exception
dalam cuplikan kode di atas? (Jika saya ingin menambahkannya, IntelliJ memperingatkan saya bahwa Exception
tidak pernah dibuang).
javac
- Saya telah menemukan kasus di mana kompiler Eclipse lebih lunak.-source 1.6
bendera memunculkan kesalahan kompilasi seperti yang diharapkan. Kompilasi dengan kompatibilitas sumber 7 tidak meningkatkan kesalahan kompilasiIn 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.
Jawaban:
Saya belum memindai melalui
JLS
seperti yang Anda tanyakan dalam pertanyaan Anda, jadi silakan ambil jawaban ini dengan sebutir garam. Saya ingin berkomentar, tapi itu terlalu besar.Saya menemukan lucu kadang-kadang, betapa
javac
cantik "pintar" dalam beberapa kasus (seperti dalam kasus Anda), tetapi meninggalkan banyak hal lain untuk ditangani nantiJIT
. Dalam hal ini, hanya saja kompiler "dapat mengatakan" bahwa hanya yangRuntimeException
akan ditangkap. Ini jelas, itu satu-satunya hal yang Anda lemparkandoSomething
. Jika Anda sedikit mengubah kode Anda menjadi:Anda akan melihat perilaku yang berbeda, karena sekarang
javac
dapat mengatakan bahwa ada yang baruException
Anda lempar, tidak terkait dengan yang Anda tangkap.Tetapi hal-hal yang jauh dari ideal, Anda dapat "menipu" kompiler lagi melalui:
IMO, karena
ex2 = ex;
itu seharusnya tidak gagal lagi, tetapi ternyata gagal.Kalau-kalau ini dikompilasi dengan
javac 13+33
sumber
ex2
pengecualian akan dibuang, itu awalnya dibuat sebagaiException
tetapi kemudian dipindahkanex
, dan oleh karena itu kompiler tidak dapat menjadi pintar.JLS
mungkin datang dan memberikan kutipan yang diperlukan untuk membuktikan ini; sayangnya saya tidak memilikinya.ex = ex;
), heuristik tidak lagi diterapkan. Perilaku ini tampaknya diterapkan untuk semua level sumber dari 7 hingga 11 dan mungkin 13