Bagaimana menangani pengecualian yang diperiksa yang tidak pernah dapat dilempar

35

Contoh:

foobar = new InputStreamReader(p.getInputStream(), "ISO-8859-1");

Karena pengkodean hardcoded dan benar, konstruktor tidak akan pernah membuang UnsupportedEncodingException dideklarasikan dalam spesifikasi (kecuali jika implementasi java rusak, dalam hal ini saya tetap hilang). Bagaimanapun, Java memaksa saya untuk berurusan dengan pengecualian itu.

Saat ini, kelihatannya seperti itu

try {
    foobar = new InputStreamReader(p.getInputStream(), "ISO-8859-1");
}
catch(UnsupportedEncodingException e) { /* won't ever happen */ }

Ada ide bagaimana membuatnya lebih baik?

pengguna281377
sumber
4
Untuk tantangan nyata, tulis unit test untuk memastikan tangkapan ini benar-benar berfungsi.
Jay Elston
1
`melempar ImpossibleException baru (" Reboot alam semesta. Hal-hal kacau ", e);
Chris Cudmore
1
"Tidak akan pernah terjadi" akan ...
Thorbjørn Ravn Andersen: mungkin setelah mengubah program, tetapi setidaknya dalam kondisi saat ini, tidak ada set data input yang dapat memicu pengecualian.
user281377
Dalam contoh spesifik Anda di atas, pengecualian dilemparkan karena metode ini tidak tahu apakah akan dipanggil dengan pengkodean yang didukung atau tidak. Cara mengatasi hal ini adalah jika ada konstruktor lain yang mengasumsikan charset standar diberikan, yang tidak perlu menyatakan bahwa pengecualian akan dilempar.
jordan

Jawaban:

27

Kebiasaan saya adalah, hanya untuk berada di sisi yang aman, untuk menempatkan sebuah assertke blok penangkap. Seseorang mungkin mengubah isi tryblok nanti, dan Anda ingin tahu apakah kode gagal, bukan?

Péter Török
sumber
Ide bagus; sederhana assert false;tidak menambahkan terlalu banyak kekacauan dan memperjelas bahwa saya menganggap catch catch block tidak akan pernah dimasukkan.
user281377
5
@ammoQ, atau Anda bahkan dapat menambahkan pesan untuk membuat Anda maksud benar-benar jelas: assert false : "should never happen".
Péter Török
13
Lebih baik lagi, "Seharusnya tidak pernah terjadi karena Java membutuhkan charset ISO-8859-1 untuk didukung."
dan04
3
assertmenyiratkan pernyataan diaktifkan. Saya melempar UnexpectedException(yang juga memiliki manfaat membiarkan saya memiliki jejak tumpukan ...).
Potong
35

Jika saya diberi sen untuk setiap kali saya melihat log / kesalahan, "Ini seharusnya tidak pernah terjadi", saya akan ... yah, dua sen. Tetapi tetap saja...

Blok tangkapan kosong membuat indera laba-laba saya tergelitik dan alat penganalisa kode yang paling baik mengeluh. Saya akan menghindari apa pun untuk membiarkan mereka kosong. Tentu, sekarang Anda tahu kesalahan tidak pernah bisa terjadi, tetapi setahun dari sekarang seseorang melakukan pencarian global-ganti "ISO-8859-1" dan tiba-tiba Anda mungkin memiliki bug yang sangat sulit ditemukan.

The assert falsesaran adalah baik, tapi karena pernyataan dapat dinonaktifkan saat runtime, mereka tidak ada jaminan. Saya akan menggunakan sebagai RuntimeExceptiongantinya. Mereka tidak harus ditangkap dengan memanggil kelas dan jika pernah terjadi Anda akan memiliki jejak tumpukan untuk memberikan informasi lengkap.

Fredrik
sumber
28

Saya selalu melakukannya seperti ini:

try {
    foobar = new InputStreamReader(p.getInputStream(), "ISO-8859-1");
} catch(UnsupportedEncodingException e) {
    throw new AssertionError(e);
}

Mungkin sedikit bertele-tele (Java is ...), tetapi setidaknya Anda akan mendapatkan kesalahan pernyataan ketika hal yang mustahil terjadi.

Jika implementasi Java rusak, Anda ingin mendapatkan pesan kesalahan sebagus mungkin, secepat mungkin, alih-alih mengabaikan yang tidak mungkin. Dan bahkan jika implementasi Java tidak rusak, seseorang dapat mengubah kode Anda menjadi "UTF8"(oops - seharusnya "UTF-8"?).

Ini seharusnya merupakan pengecualian runtime sejak awal. JDK penuh dengan pilihan yang salah semacam ini.

Joonas Pulakka
sumber
4
Decision buruk sebenarnya (atau kelalaian jika Anda mau) adalah bahwa tidak ada instance Charset yang telah ditentukan sebelumnya untuk 6 charset yang diperlukan Java. foobar = new InputStreamReader(p.getInputStream(), Charset.ISO_8859_1);- sekarang bukankah itu lebih baik dan menghindari kesalahan sekali dan selamanya?
user281377
3
Perpustakaan Guava memiliki banyak hal ini. Membuat hidup lebih mudah.
3
Java 1.7+ memang memiliki konstanta charset yang didefinisikan: docs.oracle.com/javase/7/docs/api/java/nio/charset/… . Gunakan seperti ini: StandardCharsets.UTF_8.displayName ()
Michael
4

Jika Anda adalah satu-satunya pengembang yang akan pernah melihat kode ini maka saya akan mengatakan itu baik-baik saja, tetapi jika tidak, saya akan memperlakukannya sebagai kemungkinan nyata atau setidaknya mengubah komentar "tidak akan pernah terjadi" menjadi sesuatu yang lebih bermanfaat.

Thanos Papathanasiou
sumber
4

Bagian dari perkecualian ini yang paling mengganggu saya adalah bahwa hal itu mengganggu cakupan kode saya.

Ketika saya menjadi kompulsif tentang cakupan, saya akan menggulung mencoba / menangkap bahwa "tidak pernah bisa terjadi" (... atau hanya jika saya menggunakan JVM mutan yang entah bagaimana lupa untuk memasukkan "US-ASCII") ke dalam kelas dan metode yang merangkum yang mencoba / menangkap, dan mengganti pengecualian yang diperiksa di salah satu cara yang disebutkan di sini (biasanya melempar pengecualian yang tidak dicentang dengan pesan snide).

Kemudian jangkauan kode saya mendapatkan hit di kelas utilitas, tetapi tidak dalam semua referensi untuk operasi yang tersebar di sekitar kode saya.

Kadang-kadang saya akan mengambil waktu untuk menggulung seperti operasi di kelas yang sebenarnya memiliki semantik yang koheren. Tapi karena itu sangat jelas bagi rekan tim saya apa yang terjadi, saya biasanya hanya membuatnya sesederhana mungkin & tidak terlalu khawatir tentang desain terbaik.

Namun, seperti komentar yang disebutkan, Guava & perpustakaan lain memiliki cara untuk mengurangi rasa sakit ini - tetapi itu pada dasarnya strategi yang sama. Pindahkan gangguan di luar panggung agar kode utama Anda tidak terkena dampak liputan.

rampok
sumber