TL; DR
Premis
- Pengecualian runtime harus dilemparkan ketika kesalahan tidak dapat dipulihkan: ketika kesalahan ada dalam kode, dan tidak tergantung pada keadaan eksternal (dengan demikian, pemulihan akan memperbaiki kode).
- Pengecualian yang diperiksa harus dilemparkan ketika kode itu benar, tetapi keadaan eksternal tidak seperti yang diharapkan: tidak ada konektivitas jaringan, file tidak ditemukan atau rusak, dll.
Kesimpulan
Kami dapat mengubah kembali pengecualian yang diperiksa sebagai pengecualian runtime jika kode propagasi atau antarmuka mengasumsikan bahwa implementasi yang mendasarinya bergantung pada keadaan eksternal, padahal jelas tidak.
Bagian ini membahas topik kapan salah satu pengecualian harus dilemparkan. Anda dapat melompat ke bilah horizontal berikutnya jika Anda hanya ingin membaca penjelasan yang lebih rinci untuk kesimpulannya.
Kapan tepat untuk melempar pengecualian runtime? Anda melempar pengecualian runtime ketika jelas bahwa kode tersebut salah, dan pemulihan itu sesuai dengan memodifikasi kode.
Misalnya, layak untuk melemparkan pengecualian runtime untuk yang berikut:
float nan = 1/0;
Ini akan membuang pembagian dengan pengecualian runtime nol. Ini sesuai karena kodenya rusak.
Atau misalnya, inilah sebagian HashMap
konstruktor:
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
// more irrelevant code...
}
Untuk memperbaiki kapasitas awal atau faktor muatan, sebaiknya Anda mengedit kode untuk memastikan bahwa nilai yang benar dilewatkan. Ini tidak tergantung pada beberapa server jauh yang sedang naik, pada kondisi saat ini dari disk, file, atau program lain. Konstruktor yang dipanggil dengan argumen yang tidak valid tergantung pada kebenaran kode panggilan, apakah itu perhitungan yang salah yang menyebabkan parameter tidak valid atau aliran yang tidak pantas yang melewatkan kesalahan.
Kapan tepat untuk melemparkan pengecualian yang diperiksa? Anda melempar pengecualian yang dicentang saat masalah dapat dipulihkan tanpa mengubah kode. Atau dengan kata lain, Anda melemparkan pengecualian yang diperiksa saat kesalahan terkait dengan status sementara kode tersebut benar.
Sekarang kata "pulih" mungkin rumit di sini. Ini bisa berarti bahwa Anda menemukan cara lain untuk mencapai tujuan: Misalnya, jika server tidak merespons maka Anda harus mencoba server berikutnya. Jika pemulihan semacam itu memungkinkan untuk kasus Anda, maka itu hebat, tapi itu bukan satu-satunya cara pemulihan - pemulihan bisa saja menampilkan dialog kesalahan kepada pengguna yang menjelaskan apa yang terjadi, atau jika itu adalah aplikasi server maka itu bisa saja mengirim email ke administrator, atau bahkan sekadar mencatat kesalahan dengan tepat dan singkat.
Mari kita ambil contoh yang disebutkan dalam jawaban mrmuggles:
public void dataAccessCode(){
try{
..some code that throws SQLException
}catch(SQLException ex){
throw new RuntimeException(ex);
}
}
Ini bukan cara yang benar untuk menangani pengecualian yang diperiksa. Ketidakmampuan belaka untuk menangani pengecualian dalam hal ini lingkup metode ini tidak berarti bahwa aplikasi harus jatuh. Sebagai gantinya, adalah tepat untuk menyebarkannya ke ruang lingkup yang lebih tinggi seperti:
public Data dataAccessCode() throws SQLException {
// some code that communicates with the database
}
Yang memungkinkan untuk kemungkinan pemulihan oleh penelepon:
public void loadDataAndShowUi() {
try {
Data data = dataAccessCode();
showUiForData(data);
} catch(SQLException e) {
// Recover by showing an error alert dialog
showCantLoadDataErrorDialog();
}
}
Pengecualian yang dicek adalah alat analisis statis, mereka menjelaskan kepada programmer apa yang bisa salah dalam panggilan tertentu tanpa mengharuskan mereka mempelajari implementasinya atau melalui proses coba-coba. Ini membuatnya mudah untuk memastikan bahwa tidak ada bagian dari aliran kesalahan akan diabaikan. Memikirkan kembali pengecualian yang dicentang sebagai pengecualian runtime berfungsi terhadap fitur analisis statis hemat tenaga kerja ini.
Perlu juga disebutkan bahwa lapisan pemanggil memiliki konteks yang lebih baik dari skema yang lebih besar seperti yang telah ditunjukkan di atas. Mungkin ada banyak penyebab yang dataAccessCode
akan dipanggil, alasan spesifik untuk panggilan tersebut hanya dapat dilihat oleh penelepon - sehingga dapat membuat keputusan yang lebih baik pada pemulihan yang benar atas kegagalan.
Sekarang kita sudah memiliki perbedaan ini dengan jelas, kita dapat melanjutkan untuk menyimpulkan ketika ok untuk rethrow pengecualian diperiksa sebagai pengecualian runtime.
Mengingat hal di atas, kapankah pantas untuk melakukan rethrow eksepsi yang dicentang sebagai RuntimeException? Ketika kode yang Anda gunakan mengasumsikan ketergantungan pada keadaan eksternal, tetapi Anda dapat dengan jelas menyatakan bahwa itu tidak tergantung pada keadaan eksternal.
Pertimbangkan yang berikut ini:
StringReader sr = new StringReader("{\"test\":\"test\"}");
try {
doesSomethingWithReader(sr); // calls #read, so propagates IOException
} catch (IOException e) {
throw new IllegalStateException(e);
}
Dalam contoh ini, kode tersebut menyebar IOException
karena API Reader
dirancang untuk mengakses keadaan eksternal, namun kita tahu bahwa StringReader
implementasi tidak mengakses keadaan eksternal. Pada ruang lingkup ini, di mana kita dapat dengan pasti menyatakan bahwa bagian-bagian yang terlibat dalam panggilan tidak mengakses IO atau keadaan eksternal lainnya, kita dapat dengan aman memikirkan kembali pengecualian sebagai pengecualian runtime tanpa kolega yang menakjubkan yang tidak mengetahui implementasi kita (dan mungkin dengan asumsi bahwa kode pengaksesan IO akan membuang a IOException
).
Alasan untuk menjaga pengecualian pengecualian keadaan eksternal secara ketat diperiksa adalah bahwa pengecualian itu tidak deterministik (tidak seperti pengecualian yang bergantung pada logika, yang dapat diprediksi akan direproduksi setiap waktu untuk versi kode). Misalnya, jika Anda mencoba untuk membagi dengan 0, Anda akan selalu menghasilkan pengecualian. Jika Anda tidak membagi dengan 0, Anda tidak akan pernah menghasilkan pengecualian, dan Anda tidak harus menangani kasus pengecualian itu, karena itu tidak akan pernah terjadi. Namun, dalam hal mengakses file, berhasil sekali tidak berarti Anda akan berhasil di lain waktu - pengguna mungkin telah mengubah izin, proses lain mungkin telah menghapus atau memodifikasinya. Jadi, Anda selalu harus menangani kasus luar biasa itu, atau Anda mungkin memiliki bug.