Menangkap Pengecualian dan menyusun kembali, tapi itu bukan Pengecualian

10

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 Exceptiondan kemudian memunculkannya kembali, tetapi metode ini tidak dinyatakan untuk melempar Exceptiondan tampaknya tidak perlu. Kode ini mengkompilasi dengan baik (setidaknya dalam Java 11).

Harapan saya adalah bahwa saya harus mendeklarasikan throws Exceptiondalam run()-metode.

Informasi tambahan

Dengan cara yang sama, jika doSomethingdideklarasikan untuk melempar IOExceptionmaka hanya IOExceptionperlu dideklarasikan dalam run()-metode, meskipun Exceptionditangkap 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 Exceptiondalam cuplikan kode di atas? (Jika saya ingin menambahkannya, IntelliJ memperingatkan saya bahwa Exceptiontidak pernah dibuang).

Simon Forsberg
sumber
3
Menarik. Kompiler apa yang Anda gunakan? Jika itu adalah kompiler IDE, maka tanyakan javac- Saya telah menemukan kasus di mana kompiler Eclipse lebih lunak.
M. Prokhorov
2
Saya dapat mereproduksi perilaku ini di openjdk-8. Khusus mengkompilasi dengan -source 1.6bendera memunculkan kesalahan kompilasi seperti yang diharapkan. Kompilasi dengan kompatibilitas sumber 7 tidak meningkatkan kesalahan kompilasi
Vogel612
1
Sepertinya kompiler lebih pintar sejak Java 7 dan melakukan lebih banyak pengecekan pada pengecualian aktual yang dapat dibuang.
michalk
2
Pertanyaan ini bukan duplikat dan jawabannya dapat ditemukan di tautan yang saya berikanIn 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
The duplikat saat ini ditandai pasti relevan, tetapi tidak memberikan cukup jawaban rinci IMO. Ada satu tautan ke JLS di komentar ke jawaban di sana, selain itu tidak ada informasi.
Simon Forsberg

Jawaban:

0

Saya belum memindai melalui JLSseperti 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 javaccantik "pintar" dalam beberapa kasus (seperti dalam kasus Anda), tetapi meninggalkan banyak hal lain untuk ditangani nanti JIT. Dalam hal ini, hanya saja kompiler "dapat mengatakan" bahwa hanya yang RuntimeExceptionakan ditangkap. Ini jelas, itu satu-satunya hal yang Anda lemparkan doSomething. Jika Anda sedikit mengubah kode Anda menjadi:

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

Anda akan melihat perilaku yang berbeda, karena sekarang javacdapat mengatakan bahwa ada yang baru ExceptionAnda lempar, tidak terkait dengan yang Anda tangkap.

Tetapi hal-hal yang jauh dari ideal, Anda dapat "menipu" kompiler lagi melalui:

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

IMO, karena ex2 = ex;itu seharusnya tidak gagal lagi, tetapi ternyata gagal.

Kalau-kalau ini dikompilasi dengan javac 13+33

Eugene
sumber
Saya membaca di beberapa tautan bahwa seseorang asalkan jika Anda menetapkan kembali pengecualian yang ditangkap di blok tangkap, maka kompiler tidak dapat menjadi pintar. Saya berasumsi sesuatu yang serupa berlaku dalam kasus ini. Kompiler tahu bahwa ex2pengecualian akan dibuang, itu awalnya dibuat sebagai Exceptiontetapi kemudian dipindahkan ex, dan oleh karena itu kompiler tidak dapat menjadi pintar.
Simon Forsberg
@SimonForsberg seseorang yang memiliki hasrat untuk JLSmungkin datang dan memberikan kutipan yang diperlukan untuk membuktikan ini; sayangnya saya tidak memilikinya.
Eugene
Sebagai catatan, ketika saya mengubah blok tangkap berisi penugasan kembali pengecualian yang tertangkap untuk dirinya sendiri ( ex = ex;), heuristik tidak lagi diterapkan. Perilaku ini tampaknya diterapkan untuk semua level sumber dari 7 hingga 11 dan mungkin 13
Vogel612
Lihat pertanyaan ini yang juga merupakan duplikat. Yang ini dan dup dari dup yang mungkin menjelaskannya dan juga tautan ke JLS.
michalk