Selama beberapa tahun sekarang saya tidak bisa mendapatkan jawaban yang layak untuk pertanyaan berikut: mengapa beberapa pengembang menentang pengecualian yang diperiksa? Saya telah melakukan banyak percakapan, membaca berbagai hal di blog, membaca apa yang dikatakan Bruce Eckel (orang pertama yang saya lihat berbicara menentang mereka).
Saat ini saya sedang menulis beberapa kode baru dan sangat memperhatikan bagaimana saya menangani pengecualian. Saya mencoba melihat sudut pandang kerumunan "kami tidak suka memeriksa pengecualian" dan saya masih tidak bisa melihatnya.
Setiap percakapan yang saya akhiri dengan pertanyaan yang sama tidak terjawab ... izinkan saya mengaturnya:
Secara umum (dari bagaimana Java dirancang),
Error
adalah untuk hal-hal yang tidak boleh ditangkap (VM memiliki alergi kacang dan seseorang menjatuhkan toples kacang di atasnya)RuntimeException
adalah untuk hal-hal yang tidak dilakukan oleh programmer (programmer berjalan di akhir array)Exception
(KecualiRuntimeException
) untuk hal-hal yang di luar kendali programmer (disk terisi saat menulis ke sistem file, batas pegangan file untuk proses telah tercapai dan Anda tidak dapat membuka file lagi)Throwable
hanyalah induk dari semua jenis pengecualian.
Argumen umum yang saya dengar adalah bahwa jika pengecualian terjadi maka semua pengembang akan lakukan adalah keluar dari program.
Argumen umum lain yang saya dengar adalah bahwa pengecualian yang diperiksa membuatnya lebih sulit untuk memperbaiki kode.
Untuk argumen "yang akan saya lakukan adalah keluar", saya katakan bahwa meskipun Anda keluar, Anda perlu menampilkan pesan kesalahan yang masuk akal. Jika Anda hanya mengayuh pada penanganan kesalahan maka pengguna Anda tidak akan terlalu senang ketika program keluar tanpa indikasi yang jelas mengapa.
Untuk kerumunan "itu membuatnya sulit untuk refactor", itu menunjukkan bahwa tingkat abstraksi yang tepat tidak dipilih. Alih-alih mendeklarasikan metode melempar IOException
, IOException
harus diubah menjadi pengecualian yang lebih cocok untuk apa yang sedang terjadi.
Saya tidak memiliki masalah dengan membungkus Main dengan catch(Exception)
(atau dalam beberapa kasus catch(Throwable)
untuk memastikan bahwa program dapat keluar dengan anggun - tapi saya selalu menangkap pengecualian khusus yang saya butuhkan. Melakukan itu memungkinkan saya untuk, setidaknya, menampilkan yang sesuai pesan eror.
Pertanyaan yang tidak pernah dibalas orang adalah ini:
Jika Anda melempar
RuntimeException
subclass, bukanException
subclass, bagaimana Anda tahu apa yang seharusnya Anda tangkap?
Jika jawabannya adalah menangkap Exception
maka Anda juga berurusan dengan kesalahan programmer dengan cara yang sama seperti pengecualian sistem. Sepertinya itu salah bagi saya.
Jika Anda menangkap Throwable
maka Anda memperlakukan pengecualian sistem dan kesalahan VM (dan sejenisnya) dengan cara yang sama. Sepertinya itu salah bagi saya.
Jika jawabannya adalah Anda hanya menangkap pengecualian yang Anda tahu dilemparkan maka bagaimana Anda tahu yang dilemparkan? Apa yang terjadi ketika programmer X melempar pengecualian baru dan lupa untuk menangkapnya? Sepertinya itu sangat berbahaya bagi saya.
Saya akan mengatakan bahwa program yang menampilkan jejak stack salah. Apakah orang yang tidak suka memeriksa pengecualian tidak merasa seperti itu?
Jadi, jika Anda tidak menyukai pengecualian yang dicentang, dapatkah Anda menjelaskan mengapa tidak DAN menjawab pertanyaan yang tidak dijawab?
Sunting: Saya tidak mencari saran kapan harus menggunakan model mana pun, apa yang saya cari adalah mengapa orang memperluas dari RuntimeException
karena mereka tidak suka memperpanjang dari Exception
dan / atau mengapa mereka menangkap pengecualian dan kemudian menyusun kembali RuntimeException
alih - alih menambahkan lemparan ke metode mereka. Saya ingin memahami motivasi untuk tidak menyukai pengecualian yang diperiksa.
sumber
Jawaban:
Saya pikir saya membaca wawancara Bruce Eckel yang sama dengan yang Anda lakukan - dan itu selalu mengganggu saya. Bahkan, argumen dibuat oleh orang yang diwawancarai (jika ini memang posting yang sedang Anda bicarakan) Anders Hejlsberg, jenius MS di belakang .NET dan C #.
Meskipun saya penggemar Hejlsberg dan karyanya, argumen ini selalu mengejutkan saya. Pada dasarnya bermuara pada:
Dengan "jika tidak disajikan kepada pengguna" maksud saya jika Anda menggunakan pengecualian runtime programmer malas hanya akan mengabaikannya (dibandingkan menangkapnya dengan blok tangkapan kosong) dan pengguna akan melihatnya.
Ringkasan dari argumen adalah bahwa "Programmer tidak akan menggunakannya dengan benar dan tidak menggunakannya dengan benar lebih buruk daripada tidak memilikinya" .
Ada beberapa kebenaran dalam argumen ini dan pada kenyataannya, saya menduga motivasi Gosling untuk tidak menempatkan operator override di Jawa berasal dari argumen serupa - mereka membingungkan programmer karena mereka sering disalahgunakan.
Tetapi pada akhirnya, saya menemukan argumen palsu Hejlsberg dan mungkin post-hoc yang dibuat untuk menjelaskan kekurangan dan bukan keputusan yang dipikirkan dengan matang.
Saya berpendapat bahwa sementara terlalu banyak menggunakan pengecualian diperiksa adalah hal yang buruk dan cenderung mengarah pada penanganan yang ceroboh oleh pengguna, tetapi penggunaan yang tepat dari mereka memungkinkan programmer API untuk memberikan manfaat besar bagi programmer klien API.
Sekarang programmer API harus berhati-hati untuk tidak membuang pengecualian yang diperiksa di semua tempat, atau mereka hanya akan mengganggu programmer klien. Programmer klien yang sangat malas akan berusaha untuk menangkap
(Exception) {}
ketika Hejlsberg memperingatkan dan semua manfaat akan hilang dan neraka akan terjadi. Tetapi dalam beberapa keadaan, tidak ada pengganti untuk pengecualian yang diperiksa.Bagi saya, contoh klasik adalah API buka file. Setiap bahasa pemrograman dalam sejarah bahasa (setidaknya pada sistem file) memiliki API di suatu tempat yang memungkinkan Anda membuka file. Dan setiap programmer klien yang menggunakan API ini tahu bahwa mereka harus berurusan dengan kasus bahwa file yang mereka coba buka tidak ada. Biarkan saya ulangi bahwa: Setiap programmer klien yang menggunakan API ini harus tahu bahwa mereka harus berurusan dengan kasus ini. Dan ada intinya: dapatkah programmer API membantu mereka tahu bahwa mereka harus menghadapinya melalui komentar saja atau dapatkah mereka memaksa klien untuk menghadapinya.
Dalam C idiomnya seperti
di mana
fopen
menunjukkan kegagalan dengan mengembalikan 0 dan C (bodoh) memungkinkan Anda memperlakukan 0 sebagai boolean dan ... Pada dasarnya, Anda mempelajari idiom ini dan Anda baik-baik saja. Tetapi bagaimana jika Anda seorang noob dan Anda tidak belajar idiom itu. Kemudian, tentu saja, Anda mulai dengandan belajar dengan cara yang sulit.
Perhatikan bahwa kita hanya berbicara tentang bahasa yang sangat diketik di sini: Ada gagasan yang jelas tentang apa itu API dalam bahasa yang sangat diketik: Ini adalah smorgasbord fungsionalitas (metode) untuk Anda gunakan dengan protokol yang jelas untuk masing-masing bahasa.
Protokol yang didefinisikan dengan jelas biasanya ditentukan oleh metode tanda tangan. Di sini fopen mengharuskan Anda memberikannya string (atau char * dalam kasus C). Jika Anda memberikannya sesuatu yang lain, Anda mendapatkan kesalahan waktu kompilasi. Anda tidak mengikuti protokol - Anda tidak menggunakan API dengan benar.
Dalam beberapa bahasa (tidak jelas) jenis kembali juga merupakan bagian dari protokol. Jika Anda mencoba memanggil yang setara
fopen()
dalam beberapa bahasa tanpa menugaskannya ke variabel Anda juga akan mendapatkan kesalahan waktu kompilasi (Anda hanya dapat melakukannya dengan fungsi yang tidak berlaku).Poin yang saya coba sampaikan adalah: Dalam bahasa yang diketik secara statis, programmer API mendorong klien untuk menggunakan API dengan benar dengan mencegah kode klien mereka dari kompilasi jika itu membuat kesalahan yang jelas.
(Dalam bahasa yang diketik secara dinamis, seperti Ruby, Anda dapat melewatkan apa pun, ucapkan float, sebagai nama file - dan itu akan dikompilasi. Mengapa merepotkan pengguna dengan pengecualian yang diperiksa jika Anda bahkan tidak akan mengontrol argumen metode. argumen yang dibuat di sini hanya berlaku untuk bahasa yang diketik secara statis.)
Jadi, bagaimana dengan pengecualian yang diperiksa?
Nah, inilah salah satu API Java yang dapat Anda gunakan untuk membuka file.
Lihat tangkapan itu? Inilah tanda tangan untuk metode API itu:
Perhatikan bahwa
FileNotFoundException
ini adalah pengecualian yang dicentang .Programmer API mengatakan ini kepada Anda: "Anda dapat menggunakan konstruktor ini untuk membuat FileInputStream baru tetapi Anda
a) harus memasukkan nama file sebagai String
b) harus menerima kemungkinan bahwa file tersebut mungkin tidak ditemukan saat runtime "
Dan itulah intinya sejauh yang saya ketahui.
Kuncinya pada dasarnya adalah pertanyaan apa yang dinyatakan sebagai "Hal-hal yang di luar kendali programmer". Pikiran pertama saya adalah bahwa dia memiliki arti yang di luar kendali programmer API . Namun pada kenyataannya, pengecualian yang diperiksa bila digunakan dengan benar harus benar-benar untuk hal-hal yang berada di luar kendali programmer klien dan programmer API. Saya pikir ini adalah kunci untuk tidak menyalahgunakan pengecualian yang diperiksa.
Saya pikir file-terbuka menggambarkan hal itu dengan baik. Programmer API tahu Anda mungkin memberi mereka nama file yang ternyata tidak ada pada saat API dipanggil, dan bahwa mereka tidak akan dapat mengembalikan apa yang Anda inginkan, tetapi harus memberikan pengecualian. Mereka juga tahu bahwa ini akan terjadi secara teratur dan bahwa pemrogram klien mungkin mengharapkan nama file menjadi benar pada saat mereka menulis panggilan, tetapi mungkin salah saat runtime karena alasan di luar kendali mereka juga.
Jadi API membuatnya eksplisit: Akan ada kasus-kasus di mana file ini tidak ada pada saat Anda menelepon saya dan Anda sebaiknya berurusan dengan itu.
Ini akan lebih jelas dengan kasus-counter. Bayangkan saya sedang menulis API tabel. Saya memiliki model tabel di suatu tempat dengan API termasuk metode ini:
Sekarang sebagai programmer API saya tahu akan ada kasus-kasus di mana beberapa klien memberikan nilai negatif untuk baris atau nilai baris di luar tabel. Jadi saya mungkin tergoda untuk melempar pengecualian yang dicek dan memaksa klien untuk menanganinya:
(Tentu saja saya tidak akan menyebutnya "Diperiksa".)
Ini adalah penggunaan buruk dari pengecualian yang diperiksa. Kode klien akan penuh dengan panggilan untuk mengambil data baris, yang semuanya harus menggunakan coba / tangkap, dan untuk apa? Apakah mereka akan melaporkan kepada pengguna bahwa baris yang salah dicari? Mungkin tidak - karena apa pun UI di sekitar tampilan tabel saya, tidak boleh membiarkan pengguna masuk ke keadaan di mana baris ilegal diminta. Jadi itu adalah bug pada bagian dari programmer klien.
Programmer API masih dapat memprediksi bahwa klien akan mengkodekan bug tersebut dan harus menanganinya dengan pengecualian runtime seperti
IllegalArgumentException
.Dengan pengecualian yang dicek dalam
getRowData
, ini jelas merupakan kasus yang akan mengarah pada programmer malas Hejlsberg hanya dengan menambahkan tangkapan kosong. Ketika itu terjadi, nilai-nilai baris ilegal tidak akan jelas bahkan untuk penguji atau debugging pengembang klien, melainkan akan menyebabkan kesalahan knock-on yang sulit untuk menentukan sumber. Roket Arianne akan meledak setelah diluncurkan.Oke, jadi inilah masalahnya: Saya mengatakan bahwa pengecualian yang diperiksa
FileNotFoundException
bukan hanya hal yang baik tetapi alat penting dalam kotak alat pemrogram API untuk mendefinisikan API dengan cara yang paling berguna bagi pemrogram klien. TetapiCheckedInvalidRowNumberException
ini adalah ketidaknyamanan besar, yang mengarah ke pemrograman yang buruk dan harus dihindari. Tapi bagaimana cara membedakannya.Saya kira itu bukan ilmu pasti dan saya kira itu mendasari dan mungkin membenarkan argumen Hejlsberg sampai batas tertentu. Tapi saya tidak senang membuang bayi keluar dengan air mandi di sini, jadi izinkan saya untuk mengekstrak beberapa aturan di sini untuk membedakan pengecualian yang diperiksa dengan yang buruk:
Di luar kendali klien atau Ditutup vs Terbuka:
Pengecualian yang dicentang hanya boleh digunakan di mana kasus kesalahan tidak terkendali baik API dan pemrogram klien. Ini ada hubungannya dengan seberapa terbuka atau tertutupnya sistem. Dalam UI terbatas di mana programmer klien memiliki kontrol, katakanlah, atas semua tombol, perintah keyboard dll yang menambah dan menghapus baris dari tampilan tabel (sistem tertutup), itu adalah bug pemrograman klien jika mencoba mengambil data dari baris tidak ada. Dalam sistem operasi berbasis file di mana sejumlah pengguna / aplikasi dapat menambah dan menghapus file (sistem terbuka), dapat dibayangkan bahwa file yang diminta oleh klien telah dihapus tanpa sepengetahuan mereka sehingga mereka diharapkan untuk menanganinya .
Ubiquity:
Pengecualian yang dicentang tidak boleh digunakan pada panggilan API yang sering dilakukan oleh klien. Maksud saya dari banyak tempat dalam kode klien - tidak sering tepat waktu. Jadi kode klien tidak cenderung mencoba membuka file yang sama banyak, tetapi tampilan tabel saya mendapatkan
RowData
semua tempat dari metode yang berbeda. Secara khusus, saya akan menulis banyak kode sepertidan akan menyakitkan jika harus mencoba / menangkap setiap waktu.
Memberitahu Pengguna:
Pengecualian yang diperiksa harus digunakan dalam kasus-kasus di mana Anda dapat membayangkan pesan kesalahan yang bermanfaat disajikan kepada pengguna akhir. Ini adalah "dan apa yang akan Anda lakukan ketika itu terjadi?" pertanyaan yang saya ajukan di atas. Ini juga terkait dengan item 1. Karena Anda dapat memprediksi bahwa sesuatu di luar sistem API klien-Anda mungkin menyebabkan file tidak ada di sana, Anda dapat memberi tahu pengguna tentang hal itu:
Karena nomor baris ilegal Anda disebabkan oleh bug internal dan bukan karena kesalahan pengguna, sebenarnya tidak ada informasi berguna yang dapat Anda berikan kepada mereka. Jika aplikasi Anda tidak membiarkan pengecualian runtime masuk ke konsol, mungkin akan berakhir memberi mereka beberapa pesan buruk seperti:
Singkatnya, jika Anda tidak berpikir programmer klien Anda dapat menjelaskan pengecualian Anda dengan cara yang membantu pengguna, maka Anda mungkin tidak boleh menggunakan pengecualian yang diperiksa.
Jadi itu aturan saya. Agak dibuat-buat, dan pasti akan ada pengecualian (tolong bantu saya sempurnakan jika Anda mau). Tapi argumen utama saya adalah bahwa ada kasus-kasus seperti di
FileNotFoundException
mana pengecualian yang diperiksa sama pentingnya dan berguna bagian dari kontrak API sebagai jenis parameter. Jadi kita tidak boleh membuangnya hanya karena disalahgunakan.Maaf, bukan berarti membuat ini begitu lama dan wafel. Biarkan saya selesai dengan dua saran:
A: Pemrogram API: gunakan pengecualian yang diperiksa untuk menghemat kegunaannya. Jika ragu, gunakan pengecualian yang tidak dicentang.
B: Programer klien: biasakan membuat pengecualian terbungkus (google it) sejak awal dalam pengembangan Anda. JDK 1.4 dan yang lebih baru menyediakan konstruktor
RuntimeException
untuk ini, tetapi Anda dapat dengan mudah membuatnya sendiri. Inilah konstruktornya:Kemudian biasakan setiap kali Anda harus menangani pengecualian yang diperiksa dan Anda merasa malas (atau Anda pikir programmer API terlalu bersemangat dalam menggunakan pengecualian yang diperiksa di tempat pertama), jangan hanya menelan pengecualian, bungkus saja dan rethrow itu.
Masukkan ini ke dalam salah satu templat kode kecil IDE Anda dan gunakan saat Anda merasa malas. Dengan cara ini jika Anda benar-benar perlu menangani pengecualian yang dicentang Anda akan dipaksa untuk kembali dan menanganinya setelah melihat masalah saat runtime. Karena, percayalah pada saya (dan Anders Hejlsberg), Anda tidak akan pernah kembali ke TODO itu dalam diri Anda
sumber
Hal tentang pengecualian diperiksa adalah bahwa mereka tidak benar-benar pengecualian oleh pemahaman konsep yang biasa. Sebaliknya, mereka adalah nilai pengembalian alternatif API.
Seluruh gagasan pengecualian adalah bahwa kesalahan yang dilemparkan ke suatu tempat di sepanjang rantai panggilan dapat muncul dan ditangani oleh kode di suatu tempat lebih jauh, tanpa kode intervensi harus khawatir tentang hal itu. Pengecualian yang diperiksa, di sisi lain, mengharuskan setiap tingkat kode antara pelempar dan penangkap untuk menyatakan bahwa mereka tahu tentang semua bentuk pengecualian yang dapat melewati mereka. Ini benar-benar sedikit berbeda dalam praktiknya jika pengecualian yang dicentang hanyalah nilai pengembalian khusus yang harus diperiksa oleh penelepon. mis. [kodesemu]:
Karena Java tidak dapat melakukan nilai pengembalian alternatif, atau tupel inline sederhana sebagai nilai balik, pengecualian yang diperiksa adalah respons yang wajar.
Masalahnya adalah bahwa banyak kode, termasuk petak besar dari pustaka standar, penyalahgunaan mengecek pengecualian untuk kondisi luar biasa nyata yang mungkin ingin Anda ikuti beberapa level. Mengapa IOException bukan RuntimeException? Dalam setiap bahasa lain saya dapat membiarkan pengecualian IO terjadi, dan jika saya tidak melakukan apa-apa untuk mengatasinya, aplikasi saya akan berhenti dan saya akan mendapatkan jejak stack yang berguna untuk dilihat. Ini adalah hal terbaik yang bisa terjadi.
Mungkin dua metode dari contoh Anda ingin menangkap semua IOExeception dari seluruh proses penulisan-untuk-streaming, batalkan proses dan lompat ke kode pelaporan kesalahan; di Java Anda tidak dapat melakukannya tanpa menambahkan 'throws IOException' di setiap level panggilan, bahkan level yang mereka sendiri tidak lakukan IO. Metode seperti itu seharusnya tidak perlu tahu tentang penanganan pengecualian; harus menambahkan pengecualian pada tanda tangan mereka:
Dan kemudian ada banyak pengecualian perpustakaan yang konyol seperti:
Ketika Anda harus mengacaukan kode Anda dengan crud menggelikan seperti ini, tidak heran jika pengecualian menerima banyak kebencian, meskipun sebenarnya ini hanya desain API sederhana yang buruk.
Efek buruk tertentu lainnya adalah pada Inversion of Control, di mana komponen A memasok panggilan balik ke komponen generik B. Komponen A ingin dapat membiarkan pengecualian melempar dari panggilan baliknya kembali ke tempat di mana ia disebut komponen B, tetapi tidak dapat karena itu akan mengubah antarmuka panggilan balik yang diperbaiki oleh B. A hanya dapat melakukannya dengan membungkus pengecualian nyata dalam RuntimeException, yang merupakan penanganan boilerplate yang lebih pengecualian untuk menulis.
Memeriksa pengecualian seperti yang diterapkan di Jawa dan pustaka standarnya berarti boilerplate, boilerplate, boilerplate. Dalam bahasa yang sudah lisan ini bukan menang.
sumber
Daripada mengulangi semua (banyak) alasan terhadap pengecualian yang diperiksa, saya akan memilih satu saja. Saya telah kehilangan hitungan berapa kali saya menulis blok kode ini:
99% dari waktu saya tidak bisa berbuat apa-apa. Akhirnya, blok melakukan pembersihan yang diperlukan (atau setidaknya harus dilakukan).
Saya juga kehilangan hitungan berapa kali saya melihat ini:
Mengapa? Karena seseorang harus menghadapinya dan malas. Apakah itu salah? Tentu. Apakah itu terjadi Benar. Bagaimana jika ini merupakan pengecualian yang tidak dicentang? Aplikasi ini baru saja mati (yang lebih baik menelan pengecualian).
Dan kemudian kita memiliki kode menyebalkan yang menggunakan pengecualian sebagai bentuk kontrol aliran, seperti java.text.Format . Bzzzt. Salah. Pengguna yang memasukkan "abc" ke dalam bidang angka di formulir bukan pengecualian.
Ok, saya kira itu tiga alasan.
sumber
Saya tahu ini adalah pertanyaan lama tapi saya sudah menghabiskan waktu bergulat dengan pengecualian yang sudah diperiksa dan saya harus menambahkan sesuatu. Maafkan saya untuk panjangnya!
Daging sapi utama saya dengan pengecualian adalah mereka merusak polimorfisme. Tidak mungkin membuat mereka bermain dengan baik dengan antarmuka polimorfik.
Ambil
List
antarmuka Java yang bagus . Kami memiliki implementasi dalam memori yang umum sepertiArrayList
danLinkedList
. Kami juga memiliki kelas kerangkaAbstractList
yang membuatnya mudah untuk merancang daftar tipe baru. Untuk daftar read-only, kita hanya perlu mengimplementasikan dua metode:size()
danget(int index)
.WidgetList
Kelas contoh ini membaca beberapa objek ukuran tetap tipeWidget
(tidak ditampilkan) dari file:Dengan mengekspos Widget menggunakan
List
antarmuka yang sudah dikenal , Anda dapat mengambil item (list.get(123)
) atau mengulangi daftar (for (Widget w : list) ...
) tanpa perlu tahu tentangWidgetList
dirinya sendiri. Seseorang dapat meneruskan daftar ini ke metode standar apa saja yang menggunakan daftar generik, atau membungkusnya dalamCollections.synchronizedList
. Kode yang menggunakannya tidak perlu tahu atau tidak peduli apakah "Widget" dibuat di tempat, berasal dari array, atau dibaca dari file, atau database, atau dari seluruh jaringan, atau dari relay ruang bagian masa depan. Itu masih akan berfungsi dengan benar karenaList
antarmuka diimplementasikan dengan benar.Kecuali itu tidak. Kelas di atas tidak dikompilasi karena metode akses file dapat melempar
IOException
, pengecualian yang diperiksa yang harus Anda "tangkap atau tentukan". Anda tidak dapat menetapkannya sebagai dilemparkan - kompiler tidak akan membiarkan Anda karena itu akan melanggar kontrakList
antarmuka. Dan tidak ada cara yang berguna yangWidgetList
dapat menangani pengecualian (seperti yang akan saya jelaskan nanti).Tampaknya satu-satunya hal yang harus dilakukan adalah menangkap dan menguji kembali pengecualian yang diperiksa sebagai pengecualian yang tidak dicentang:
((Sunting: Java 8 telah menambahkan
UncheckedIOException
kelas untuk kasus ini: untuk menangkap dan mem-rethrowingIOException
melintasi batas-batas metode polimorfik. Jenis membuktikan poin saya!))Jadi pengecualian yang diperiksa tidak berfungsi dalam kasus seperti ini. Anda tidak bisa melemparnya. Ditto untuk pandai yang
Map
didukung oleh database, atau implementasijava.util.Random
terhubung ke sumber entropi kuantum melalui port COM. Segera setelah Anda mencoba melakukan novel apa pun dengan penerapan antarmuka polimorfik, konsep pengecualian yang diperiksa gagal. Tetapi pengecualian yang dicentang sangat berbahaya sehingga mereka tidak akan meninggalkan Anda dengan tenang, karena Anda masih harus menangkap dan mengolah kembali dari metode tingkat bawah, mengacaukan kode dan mengacaukan jejak tumpukan.Saya menemukan bahwa
Runnable
antarmuka di mana-mana sering dicadangkan ke sudut ini, jika itu memanggil sesuatu yang melempar pengecualian yang diperiksa. Itu tidak bisa melempar pengecualian seperti apa adanya, jadi yang bisa dilakukan hanyalah mengacaukan kode dengan menangkap dan mem-rethrowing sebagaiRuntimeException
.Sebenarnya, Anda dapat membuang pengecualian yang tidak dideklarasikan jika Anda menggunakan peretasan. JVM, pada saat dijalankan, tidak peduli dengan aturan pengecualian yang diperiksa, jadi kita hanya perlu menipu kompilator. Cara termudah untuk melakukan ini adalah dengan menyalahgunakan obat generik. Ini adalah metode saya untuk itu (nama kelas ditampilkan karena (sebelum Java 8) itu diperlukan dalam sintaks panggilan untuk metode generik):
Hore! Dengan menggunakan ini kita bisa melempar pengecualian yang diperiksa setiap kedalaman tumpukan tanpa mendeklarasikannya, tanpa membungkusnya dalam
RuntimeException
, dan tanpa mengacaukan jejak tumpukan! Menggunakan contoh "WidgetList" lagi:Sayangnya, penghinaan terakhir dari pengecualian yang diperiksa adalah bahwa kompiler menolak untuk mengizinkan Anda menangkap pengecualian yang diperiksa jika, menurut pendapatnya yang cacat, itu tidak mungkin terlempar. (Pengecualian yang tidak dicentang tidak memiliki aturan ini.) Untuk mengetahui pengecualian yang terlempar secara licik, kita harus melakukan ini:
Itu agak canggung, tetapi di sisi positifnya, itu masih sedikit lebih sederhana daripada kode untuk mengekstraksi pengecualian diperiksa yang dibungkus dengan a
RuntimeException
.Untungnya,
throw t;
pernyataan itu sah di sini, meskipun jenisnyat
dicek, berkat aturan yang ditambahkan di Jawa 7 tentang rethrowing pengecualian yang ditangkap.Ketika pengecualian yang diperiksa memenuhi polimorfisme, kasus sebaliknya juga menjadi masalah: ketika suatu metode dispesifikasi berpotensi melemparkan pengecualian yang diperiksa, tetapi implementasi yang ditimpa tidak. Sebagai contoh, kelas abstrak
OutputStream
'swrite
metode semua tentukanthrows IOException
.ByteArrayOutputStream
adalah subclass yang menulis ke array di dalam memori alih-alih sumber I / O yang sebenarnya.write
Metode yang diganti tidak dapat menyebabkanIOException
s, sehingga mereka tidak memilikithrows
klausa, dan Anda dapat memanggil mereka tanpa khawatir tentang persyaratan tangkap-atau-tentukan.Kecuali tidak selalu. Misalkan
Widget
memiliki metode untuk menyimpannya ke aliran:Mendeklarasikan metode ini untuk menerima sebuah plain
OutputStream
adalah hal yang benar untuk dilakukan, sehingga dapat digunakan secara polimorfis dengan semua jenis output: file, database, jaringan, dan sebagainya. Dan array dalam memori. Namun, dengan array dalam memori, ada persyaratan palsu untuk menangani pengecualian yang tidak dapat benar-benar terjadi:Seperti biasa, pengecualian yang diperiksa menghalangi. Jika variabel Anda dideklarasikan sebagai tipe dasar yang memiliki lebih banyak persyaratan pengecualian terbuka, Anda harus menambahkan penangan untuk pengecualian itu bahkan jika Anda tahu mereka tidak akan terjadi dalam aplikasi Anda.
Tetapi tunggu, pengecualian yang diperiksa sebenarnya sangat menjengkelkan, sehingga mereka bahkan tidak akan membiarkan Anda melakukan yang sebaliknya! Bayangkan saat ini Anda menangkap setiap panggilan yang
IOException
dilemparkanwrite
padaOutputStream
, tetapi Anda ingin mengubah tipe variabel yang dideklarasikan menjadi aByteArrayOutputStream
, kompiler akan mencaci Anda karena mencoba untuk menangkap pengecualian yang dicentang yang dikatakan tidak dapat dibuang.Aturan itu menyebabkan beberapa masalah yang tidak masuk akal. Misalnya, salah satu dari tiga
write
metodeOutputStream
yang tidak diganti olehByteArrayOutputStream
. Secara khusus,write(byte[] data)
adalah metode kenyamanan yang menulis array penuh dengan memanggilwrite(byte[] data, int offset, int length)
dengan offset 0 dan panjang array.ByteArrayOutputStream
mengganti metode tiga argumen tetapi mewarisi metode kenyamanan satu argumen apa adanya. Metode yang diwariskan melakukan hal yang tepat, tetapi termasukthrows
klausa yang tidak diinginkan . Itu mungkin sebuah kekeliruan dalam desainByteArrayOutputStream
, tetapi mereka tidak pernah bisa memperbaikinya karena itu akan merusak kompatibilitas sumber dengan kode apa pun yang menangkap pengecualian - pengecualian yang tidak pernah, tidak pernah, dan tidak akan pernah dilempar!Aturan itu mengganggu saat mengedit dan debugging juga. Misalnya, kadang-kadang saya akan mengomentari panggilan metode sementara, dan jika itu bisa melempar pengecualian diperiksa, kompiler sekarang akan mengeluh tentang keberadaan lokal
try
dancatch
blok. Jadi saya harus berkomentar juga, dan sekarang ketika mengedit kode di dalam, IDE akan masuk ke level yang salah karena{
dan}
dikomentari. Gah! Ini keluhan kecil tapi sepertinya satu-satunya pengecualian yang pernah dilakukan adalah menyebabkan masalah.Saya hampir selesai. Frustrasi terakhir saya dengan pengecualian yang diperiksa adalah bahwa di sebagian besar situs panggilan , tidak ada yang berguna yang dapat Anda lakukan dengan mereka. Idealnya ketika terjadi kesalahan, kami akan memiliki penangan khusus aplikasi yang kompeten yang dapat memberi tahu pengguna tentang masalah dan / atau mengakhiri atau mencoba ulang operasi sebagaimana mestinya. Hanya pawang tinggi di atas tumpukan yang dapat melakukan ini karena itu adalah satu-satunya yang tahu tujuan keseluruhan.
Sebagai gantinya, kami mendapatkan idiom berikut, yang merajalela sebagai cara untuk menutup kompilator:
Dalam GUI atau program otomatis pesan yang dicetak tidak akan terlihat. Lebih buruk lagi, ia melanjutkan dengan sisa kode setelah pengecualian. Apakah pengecualian itu sebenarnya bukan kesalahan? Maka jangan cetak itu. Jika tidak, sesuatu yang lain akan meledak sebentar, pada saat objek pengecualian asli akan hilang. Ungkapan ini tidak lebih baik dari BASIC
On Error Resume Next
atau PHPerror_reporting(0);
.Memanggil semacam kelas logger tidak jauh lebih baik:
Itu sama malas
e.printStackTrace();
dan masih bajak dengan kode dalam keadaan tak tentu. Plus, pilihan sistem pencatatan tertentu atau penangan lain adalah spesifik-aplikasi, jadi ini menyakitkan untuk digunakan kembali.Tapi tunggu! Ada cara mudah dan universal untuk menemukan penangan khusus aplikasi. Ini lebih tinggi dari tumpukan panggilan (atau diatur sebagai pengendali pengecualian Thread yang tidak tertangkap ). Jadi di sebagian besar tempat, yang perlu Anda lakukan adalah melemparkan pengecualian lebih tinggi ke tumpukan . Misalnya
throw e;
,. Pengecualian diperiksa hanya menghalangi.Saya yakin pengecualian yang diperiksa terdengar seperti ide yang bagus ketika bahasa dirancang, tetapi dalam praktiknya saya merasa semuanya mengganggu dan tidak bermanfaat.
sumber
RuntimeException
. Perhatikan bahwa suatu rutin dapat secara bersamaan dinyatakan sebagaithrows IOException
namun juga menentukan bahwa segala yangIOException
terlempar dari panggilan bersarang harus dianggap tidak terduga dan dibungkus.if (false)
ini. Ini menghindari masalah klausa lemparan dan peringatan membantu saya untuk menavigasi kembali lebih cepat. +++ Yang mengatakan, saya setuju dengan semua yang Anda tulis. Pengecualian yang diperiksa memiliki beberapa nilai, tetapi nilai ini dapat diabaikan jika dibandingkan dengan biayanya. Hampir selalu mereka hanya menghalangi.Yah, ini bukan tentang menampilkan stacktrace atau crash secara diam-diam. Ini tentang cara mengkomunikasikan kesalahan antar lapisan.
Masalah dengan pengecualian yang diperiksa adalah mereka mendorong orang untuk menelan detail penting (yaitu, kelas pengecualian). Jika Anda memilih untuk tidak menelan detail itu, maka Anda harus terus menambahkan deklarasi pelemparan di seluruh aplikasi Anda. Ini berarti 1) bahwa tipe pengecualian baru akan memengaruhi banyak tanda tangan fungsi, dan 2) Anda dapat melewatkan contoh spesifik dari pengecualian yang sebenarnya ingin Anda tangkap (misalnya Anda membuka file sekunder untuk fungsi yang menulis data ke File sekunder adalah opsional, sehingga Anda dapat mengabaikan kesalahannya, tetapi karena tanda tangannya
throws IOException
, mudah untuk mengabaikan ini).Saya sebenarnya berurusan dengan situasi ini sekarang dalam aplikasi. Kami mengemas ulang hampir pengecualian sebagai AppSpecificException. Ini membuat tanda tangan benar-benar bersih dan kami tidak perlu khawatir tentang meledaknya
throws
tanda tangan.Tentu saja, sekarang kita perlu mengkhususkan penanganan kesalahan di tingkat yang lebih tinggi, menerapkan logika coba lagi dan semacamnya. Semuanya adalah AppSpecificException, jadi kami tidak dapat mengatakan "Jika IOException dilempar, coba lagi" atau "Jika ClassNotFound dilempar, batalkan sepenuhnya". Kami tidak memiliki cara yang dapat diandalkan untuk sampai ke pengecualian nyata karena hal-hal akan dikemas ulang berulang kali saat mereka melewati antara kode kami dan kode pihak ketiga.
Inilah sebabnya saya penggemar berat penanganan python. Anda hanya dapat menangkap hal-hal yang Anda inginkan dan / atau dapat tangani. Segala sesuatu yang lain meluap seolah-olah Anda memainkannya kembali sendiri (yang telah Anda lakukan)
Saya telah menemukan, berkali-kali, dan sepanjang proyek yang saya sebutkan, penanganan pengecualian termasuk dalam 3 kategori:
sumber
throws
deklarasi.SNR
Pertama, pengecualian yang diperiksa mengurangi "rasio signal-to-noise" untuk kode. Anders Hejlsberg juga berbicara tentang pemrograman imperatif vs deklaratif yang merupakan konsep serupa. Pokoknya pertimbangkan potongan kode berikut:
Perbarui UI dari non-UI-utas di Java:
Perbarui UI dari non-UI-utas di C #:
Yang tampaknya jauh lebih jelas bagi saya. Ketika Anda mulai melakukan lebih banyak pekerjaan UI di Swing, pengecualian mulai menjadi sangat menjengkelkan dan tidak berguna.
Break Penjara
Untuk mengimplementasikan bahkan implementasi paling dasar, seperti antarmuka Daftar Java, pengecekan pengecualian sebagai alat untuk desain berdasarkan kontrak jatuh. Pertimbangkan daftar yang didukung oleh database atau sistem file atau implementasi lain yang melempar pengecualian yang diperiksa. Satu-satunya implementasi yang mungkin adalah menangkap pengecualian yang diperiksa dan mengolahnya kembali sebagai pengecualian yang tidak dicentang:
Dan sekarang Anda harus bertanya apa gunanya semua kode itu? Pengecualian diperiksa hanya menambah kebisingan, pengecualian telah ditangkap tetapi tidak ditangani dan desain oleh kontrak (dalam hal pengecualian diperiksa) telah rusak.
Kesimpulan
Saya menulis blog tentang ini sebelumnya .
sumber
Artima menerbitkan sebuah wawancara dengan salah satu arsitek .NET, Anders Hejlsberg, yang secara aktif membahas argumen terhadap pengecualian yang diperiksa. Pencicip singkat:
sumber
Awalnya saya setuju dengan Anda, karena saya selalu mendukung pengecualian yang dicek, dan mulai memikirkan mengapa saya tidak suka tidak memeriksa pengecualian di .Net. Tetapi kemudian saya menyadari bahwa saya tidak benar-benar menyukai pengecualian yang diperiksa.
Untuk menjawab pertanyaan Anda, ya, saya suka program saya untuk menampilkan jejak stack, terutama yang jelek. Saya ingin aplikasi meledak menjadi tumpukan pesan kesalahan paling jelek yang pernah Anda inginkan.
Dan alasannya adalah karena, jika berhasil, saya harus memperbaikinya, dan saya harus segera memperbaikinya. Saya ingin segera tahu bahwa ada masalah.
Berapa kali Anda benar-benar menangani pengecualian? Saya tidak berbicara tentang menangkap pengecualian - saya berbicara tentang menangani mereka? Terlalu mudah untuk menulis yang berikut ini:
Dan saya tahu Anda dapat mengatakan bahwa ini adalah praktik buruk, dan bahwa 'jawabannya' adalah melakukan sesuatu dengan pengecualian (biarkan saya tebak, catat itu?), Tetapi di Dunia Nyata (tm), sebagian besar programmer tidak melakukan Itu.
Jadi ya, saya tidak ingin menangkap pengecualian jika saya tidak harus melakukannya, dan saya ingin program saya meledak secara spektakuler ketika saya gagal. Gagal diam-diam adalah hasil terburuk yang mungkin terjadi.
sumber
RuntimeExceptions
yang memang tidak perlu Anda tangkap, dan saya setuju Anda harus membiarkan program ini meledak. Pengecualian yang harus selalu Anda tangkap dan tangani adalah pengecualian yang dicentang sepertiIOException
. Jika Anda mendapatkanIOException
, tidak ada yang diperbaiki dalam kode Anda; program Anda tidak boleh meledak hanya karena ada gangguan jaringan.Artikel Pengecualian Java yang Efektif menjelaskan dengan baik kapan harus menggunakan yang tidak dicentang dan kapan harus menggunakan pengecualian yang diperiksa. Berikut adalah beberapa kutipan dari artikel tersebut untuk menyoroti poin utama:
(SO tidak mengizinkan tabel, jadi Anda mungkin ingin membaca yang berikut dari halaman asli ...)
sumber
args[]
? Apakah itu suatu Kemungkinan atau Kesalahan?File#openInputStream
kembaliEither<InputStream, Problem>
- jika itu yang Anda maksud, maka kami mungkin setuju.Pendeknya:
Pengecualian adalah pertanyaan desain API. -- Tidak lebih, tidak kurang.
Argumen untuk pengecualian yang diperiksa:
Untuk memahami mengapa pengecualian yang diperiksa mungkin bukan hal yang baik, mari balikkan pertanyaan dan tanyakan: Kapan atau mengapa pengecualian yang diperiksa menarik, yaitu mengapa Anda ingin kompiler menerapkan deklarasi pengecualian?
Jawabannya jelas: Terkadang Anda perlu menangkap pengecualian, dan itu hanya mungkin jika kode yang dipanggil menawarkan kelas pengecualian khusus untuk kesalahan yang Anda minati.
Oleh karena itu, argumen untuk pengecualian yang diperiksa adalah bahwa kompiler memaksa pemrogram untuk menyatakan pengecualian yang dilemparkan, dan mudah - mudahan programmer kemudian juga akan mendokumentasikan kelas pengecualian khusus dan kesalahan yang menyebabkannya.
Pada kenyataannya, terlalu sering suatu paket
com.acme
hanya melemparAcmeException
subclass daripada yang spesifik. Penelepon kemudian perlu menangani, menyatakan, atau memberi sinyal ulangAcmeExceptions
, tetapi masih belum dapat memastikan apakah suatu peristiwaAcmeFileNotFoundError
terjadi atau tidakAcmePermissionDeniedError
.Jadi, jika Anda hanya tertarik pada
AcmeFileNotFoundError
, solusinya adalah mengajukan permintaan fitur dengan programmer ACME dan menyuruh mereka untuk mengimplementasikan, mendeklarasikan, dan mendokumentasikan subclass tersebutAcmeException
.Jadi mengapa repot?
Oleh karena itu, bahkan dengan pengecualian yang dicentang, kompiler tidak dapat memaksa programmer untuk membuang pengecualian yang berguna . Itu masih hanya masalah kualitas API.
Akibatnya, bahasa tanpa pengecualian yang dicentang biasanya tidak lebih mahal. Pemrogram mungkin tergoda untuk melempar contoh yang tidak spesifik dari
Error
kelas umum daripada kelasAcmeException
, tetapi jika mereka benar-benar peduli tentang kualitas API mereka, mereka akan belajar untuk memperkenalkannyaAcmeFileNotFoundError
.Secara keseluruhan, spesifikasi dan dokumentasi pengecualian tidak jauh berbeda dari spesifikasi dan dokumentasi, katakanlah, metode biasa. Itu juga merupakan pertanyaan desain API, dan jika seorang programmer lupa untuk mengimplementasikan atau mengekspor fitur yang bermanfaat, API perlu ditingkatkan sehingga Anda dapat bekerja dengannya dengan bermanfaat.
Jika Anda mengikuti alur penalaran ini, seharusnya sudah jelas bahwa "kerumitan" menyatakan, menangkap, dan melempar kembali pengecualian yang begitu umum dalam bahasa seperti Java sering menambah sedikit nilai.
Perlu dicatat juga bahwa Java VM tidak memiliki pengecekan pengecualian - hanya Java compiler yang memeriksanya, dan file kelas dengan deklarasi pengecualian yang berubah kompatibel pada saat dijalankan. Keamanan VM Java tidak ditingkatkan dengan pengecualian yang diperiksa, hanya gaya pengkodean.
sumber
AcmeException
daripadaAcmeFileNotFoundError
dan semoga berhasil mengetahui apa yang Anda lakukan salah dan di mana Anda harus menangkapnya. Pengecualian yang dicentang menyediakan sejumlah kecil perlindungan bagi programmer terhadap desain API yang buruk.Saya telah bekerja dengan beberapa pengembang dalam tiga tahun terakhir dalam aplikasi yang relatif kompleks. Kami memiliki basis kode yang cukup sering menggunakan Pengecualian Tercatat dengan penanganan kesalahan yang tepat, dan beberapa lainnya tidak.
Sejauh ini, saya merasa lebih mudah untuk bekerja dengan basis kode dengan Pengecualian yang Diperiksa. Ketika saya menggunakan API orang lain, alangkah baiknya saya bisa melihat kondisi kesalahan seperti apa yang bisa saya harapkan ketika saya memanggil kode dan menanganinya dengan benar, baik dengan masuk, menampilkan atau mengabaikan (Ya, ada kasus yang valid untuk mengabaikan pengecualian, seperti implementasi ClassLoader). Itu memberi kode saya menulis kesempatan untuk pulih. Semua pengecualian runtime yang saya sebarkan ke atas sampai di-cache dan ditangani dengan beberapa kode penanganan kesalahan umum. Ketika saya menemukan pengecualian yang diperiksa yang tidak benar-benar ingin saya tangani pada tingkat tertentu, atau bahwa saya menganggap kesalahan logika pemrograman, maka saya membungkusnya menjadi RuntimeException dan membiarkannya muncul. Jangan pernah menelan pengecualian tanpa alasan yang bagus (dan alasan bagus untuk melakukan ini agak langka)
Ketika saya bekerja dengan basis kode yang tidak memeriksa pengecualian, itu membuat saya sedikit lebih sulit untuk mengetahui sebelumnya apa yang dapat saya harapkan ketika memanggil fungsi, yang dapat merusak beberapa hal dengan sangat buruk.
Ini tentu saja masalah preferensi dan keterampilan pengembang. Kedua cara pemrograman dan penanganan kesalahan bisa sama efektif (atau tidak efektif), jadi saya tidak akan mengatakan bahwa ada The One Way.
Secara keseluruhan, saya merasa lebih mudah untuk bekerja dengan Pengecualian Tercentang, khususnya dalam proyek besar dengan banyak pengembang.
sumber
Kategori pengecualian
Ketika berbicara tentang pengecualian, saya selalu merujuk kembali ke artikel blog pengecualian Vexing Eric Lippert . Dia menempatkan pengecualian ke dalam kategori ini:
OutOfMemoryError
atauThreadAbortException
.ArrayIndexOutOfBoundsException
,,NullPointerException
atau apa sajaIllegalArgumentException
.NumberFormatException
dariInteger.parseInt
alih-alih menyediakanInteger.tryParseInt
metode yang mengembalikan boolean false pada kegagalan parse.FileNotFoundException
,.Pengguna API:
Pengecualian diperiksa
Fakta bahwa pengguna API harus menangani pengecualian tertentu adalah bagian dari kontrak metode antara penelepon dan callee. Kontrak tersebut menetapkan, antara lain: jumlah dan jenis argumen yang diharapkan callee, jenis nilai pengembalian yang bisa diharapkan si penelepon, dan pengecualian yang diharapkan untuk ditangani si penelepon .
Karena pengecualian menjengkelkan tidak boleh ada dalam API, hanya pengecualian eksogen ini yang harus diperiksa pengecualian untuk menjadi bagian dari kontrak metode. Relatif sedikit pengecualian yang eksogen , jadi API apa pun seharusnya memiliki relatif sedikit pengecualian yang diperiksa.
Pengecualian yang diperiksa adalah pengecualian yang harus ditangani . Menangani pengecualian bisa sesederhana menelannya. Sana! Pengecualian ditangani. Titik. Jika pengembang ingin menanganinya seperti itu, baiklah. Tapi dia tidak bisa mengabaikan pengecualian, dan telah diperingatkan.
Masalah API
Tetapi setiap API yang telah memeriksa pengecualian yang menjengkelkan dan fatal (misalnya JCL) akan memberikan tekanan yang tidak perlu pada pengguna API. Pengecualian tersebut harus ditangani, tapi entah pengecualian sangat umum sehingga tidak seharusnya pengecualian di tempat pertama, atau ada yang bisa dilakukan ketika menangani itu. Dan ini menyebabkan pengembang Java membenci pengecualian yang diperiksa.
Juga, banyak API tidak memiliki hierarki kelas pengecualian yang tepat, menyebabkan semua jenis pengecualian non-eksogen menyebabkan diwakili oleh kelas pengecualian tunggal yang diperiksa (mis
IOException
.). Dan ini juga menyebabkan pengembang Java membenci pengecualian yang diperiksa.Kesimpulan
Pengecualian eksogen adalah yang bukan salah Anda, tidak bisa dicegah, dan yang harus ditangani. Ini membentuk sebagian kecil dari semua pengecualian yang bisa dilempar. API seharusnya hanya memeriksa pengecualian eksogen , dan semua pengecualian lainnya tidak dicentang. Ini akan membuat API lebih baik, mengurangi ketegangan pada pengguna API, dan karena itu mengurangi kebutuhan untuk menangkap semua, menelan atau mengolah kembali pengecualian yang tidak dicentang.
Jadi jangan membenci Java dan pengecualiannya. Alih-alih, benci API yang terlalu sering mengecek pengecualian.
sumber
Oke ... Pengecualian yang dicentang tidak ideal dan memiliki beberapa peringatan tetapi mereka memang memiliki tujuan. Saat membuat API, ada beberapa kasus kegagalan tertentu yang merupakan kontrak dari API ini. Ketika dalam konteks bahasa yang diketik sangat statis seperti Java jika seseorang tidak menggunakan pengecualian yang diperiksa maka seseorang harus bergantung pada dokumentasi dan konvensi ad-hoc untuk menyampaikan kemungkinan kesalahan. Melakukan hal itu menghilangkan semua manfaat yang dapat dibawa oleh kompiler dalam menangani kesalahan dan Anda sepenuhnya bergantung pada kemauan baik para programmer.
Jadi, satu menghapus pengecualian diperiksa, seperti yang dilakukan dalam C #, lalu bagaimana seseorang bisa secara program dan struktural menyampaikan kemungkinan kesalahan? Bagaimana cara memberi tahu kode klien bahwa kesalahan ini dan itu dapat terjadi dan harus ditangani?
Saya mendengar segala macam kengerian ketika berhadapan dengan pengecualian yang diperiksa, mereka disalahgunakan ini pasti tapi begitu juga pengecualian yang tidak dicentang. Saya katakan tunggu beberapa tahun ketika API ditumpuk banyak lapisan dalam dan Anda akan memohon pengembalian beberapa jenis sarana terstruktur untuk menyampaikan kegagalan.
Ambil kasus ketika pengecualian dilemparkan di suatu tempat di bagian bawah lapisan API dan hanya menggelegak karena tidak ada yang tahu bahwa kesalahan ini mungkin terjadi, ini meskipun jenis kesalahan yang sangat masuk akal ketika kode panggilan melemparkannya (FileNotFoundException misalnya sebagai lawan dari VogonsTrashingEarthExcept ... dalam hal ini tidak masalah jika kita menanganinya atau tidak karena tidak ada yang tersisa untuk menanganinya).
Banyak yang berpendapat bahwa tidak dapat memuat file hampir selalu merupakan akhir dunia untuk prosesnya dan itu harus mati dengan kematian yang mengerikan dan menyakitkan. Jadi ya .. tentu ... ok .. Anda membangun API untuk sesuatu dan memuat file di beberapa titik ... Saya sebagai pengguna kata API hanya dapat menanggapi ... "Siapa yang harus Anda putuskan kapan saya program seharusnya macet! " Tentu Diberi pilihan di mana pengecualian menelan dan tidak meninggalkan jejak atau EletroFlabbingChunkFluxManifoldChuggingException dengan jejak tumpukan lebih dalam dari parit Marianna saya akan mengambil yang terakhir tanpa sedikit keraguan, tetapi apakah ini berarti bahwa itu adalah cara yang diinginkan untuk berurusan dengan pengecualian ? Tidak bisakah kita berada di suatu tempat di tengah, di mana pengecualian akan disusun kembali dan dibungkus setiap kali dilintasi ke tingkat abstraksi baru sehingga benar-benar berarti sesuatu?
Terakhir, sebagian besar argumen yang saya lihat adalah "Saya tidak ingin berurusan dengan pengecualian, banyak orang tidak ingin berurusan dengan pengecualian. Pengecualian yang diperiksa memaksa saya untuk berurusan dengan mereka, jadi saya benci pengecualian yang dicentang" Untuk menghilangkan mekanisme semacam itu sama sekali dan membuangnya ke jurang neraka goto hanya konyol dan tidak memiliki jugement dan visi.
Jika kita menghilangkan pengecekan yang dicentang, kita juga bisa menghilangkan jenis pengembalian untuk fungsi dan selalu mengembalikan variabel "jenis apa pun" ... Itu akan membuat hidup jadi lebih sederhana sekarang bukan?
sumber
Memang, pengecualian yang diperiksa di satu sisi meningkatkan kekokohan dan kebenaran program Anda (Anda dipaksa untuk membuat deklarasi yang benar dari antarmuka Anda - pengecualian metode melempar pada dasarnya adalah tipe pengembalian khusus). Di sisi lain Anda menghadapi masalah yang, karena pengecualian "menggelembung", sangat sering Anda perlu mengubah banyak metode (semua penelepon, dan penelepon penelepon, dan sebagainya) ketika Anda mengubah satu pengecualian metode melempar.
Pengecualian yang diperiksa di Jawa tidak memecahkan masalah yang terakhir; C # dan VB.NET membuang bayi dengan air mandi.
Pendekatan yang bagus yang mengambil jalan tengah dijelaskan dalam makalah OOPSLA 2005 ini (atau laporan teknis terkait .)
Singkatnya, ini memungkinkan Anda untuk mengatakan:,
method g(x) throws like f(x)
yang berarti bahwa g melempar semua pengecualian untuk melempar. Voila, periksa pengecualian tanpa masalah perubahan kaskade.Meskipun ini adalah makalah akademis, saya mendorong Anda untuk membaca (bagian dari) itu, karena ia melakukan pekerjaan yang baik untuk menjelaskan apa manfaat dan kerugian dari pengecualian yang diperiksa.
sumber
Ini bukan argumen yang menentang konsep murni pengecualian yang diperiksa, tetapi hirarki kelas yang digunakan Java untuk mereka adalah pertunjukan yang aneh. Kami selalu menyebut hal-hal itu hanya "pengecualian" - yang benar, karena spesifikasi bahasa memanggilnya juga - tetapi bagaimana pengecualian disebutkan dan diwakili dalam sistem tipe?
Oleh kelas yang
Exception
dibayangkan? Yah tidak, karenaException
s adalah pengecualian, dan juga pengecualian adalahException
s, kecuali untuk pengecualian yang bukanException
s, karena pengecualian lain sebenarnya adalahError
s, yang merupakan jenis pengecualian lain, sejenis pengecualian luar biasa yang seharusnya tidak pernah terjadi kecuali ketika itu terjadi, dan yang Anda tidak boleh menangkap kecuali kadang-kadang Anda harus. Kecuali itu tidak semua karena Anda juga dapat menentukan pengecualian lain yang bukanException
atau tidakError
tetapi hanyaThrowable
pengecualian.Manakah di antara ini yang merupakan pengecualian "dicentang"?
Throwable
s adalah pengecualian yang diperiksa, kecuali jika mereka jugaError
s, yang merupakan pengecualian yang tidak dicentang, dan kemudian adaException
s, yang jugaThrowable
s dan merupakan jenis utama dari pengecualian yang diperiksa, kecuali ada satu pengecualian untuk itu juga, yaitu jika mereka jugaRuntimeException
s, karena itulah jenis pengecualian lain yang tidak dicentang.Untuk apa
RuntimeException
? Yah seperti namanya, itu pengecualian, seperti semuaException
s, dan mereka terjadi pada saat run-time, seperti semua pengecualian sebenarnya, kecuali bahwaRuntimeException
itu luar biasa dibandingkan dengan run-time lainException
karena mereka tidak seharusnya terjadi kecuali ketika Anda membuat beberapa kesalahan konyol, meskipunRuntimeException
s tidak pernahError
s, jadi mereka untuk hal-hal yang sangat keliru tetapi sebenarnya bukanError
s. Kecuali untukRuntimeErrorException
, yang sebenarnya adalahRuntimeException
untukError
. Tapi bukankah semua pengecualian seharusnya mewakili keadaan yang salah? Ya, semuanya. Kecuali untukThreadDeath
, pengecualian yang sangat tidak biasa, karena dokumentasi menjelaskan bahwa itu adalah "kejadian normal" dan bahwa 'Error
Lagi pula, karena kita membagi semua pengecualian menjadi menengah
Error
(yang untuk pengecualian eksekusi luar biasa, jadi tidak dicentang) danException
s (yang untuk kesalahan eksekusi kurang luar biasa, jadi periksa kecuali ketika tidak), kita sekarang perlu dua berbagai jenis masing-masing dari beberapa pengecualian. Jadi kita membutuhkanIllegalAccessError
danIllegalAccessException
,InstantiationError
danInstantiationException
,NoSuchFieldError
danNoSuchFieldException
,NoSuchMethodError
danNoSuchMethodException
, dan, danZipError
danZipException
.Kecuali bahwa bahkan ketika pengecualian diperiksa, selalu ada (cukup mudah) cara untuk menipu kompilator dan membuangnya tanpa diperiksa. Jika Anda melakukannya Anda mungkin mendapatkan
UndeclaredThrowableException
, kecuali dalam kasus lain, di mana itu bisa muntah sebagaiUnexpectedException
, atauUnknownException
(yang tidak terkait denganUnknownError
, yang hanya untuk "pengecualian serius"), atauExecutionException
,, atauInvocationTargetException
, atau, atauExceptionInInitializerError
.Oh, dan kita tidak boleh melupakan Java 8 yang baru
UncheckedIOException
, yang merupakanRuntimeException
pengecualian yang dirancang untuk membiarkan Anda membuang konsep pemeriksaan pengecualian ke luar jendela dengan membungkusIOException
pengecualian yang diperiksa yang disebabkan oleh kesalahan I / O (yang tidak menyebabkanIOError
pengecualian, meskipun itu ada juga) yang sangat sulit untuk ditangani sehingga Anda membutuhkannya untuk tidak diperiksa.Jawa terima kasih!
sumber
Masalah
Masalah terburuk yang saya lihat dengan mekanisme penanganan pengecualian adalah ia memperkenalkan duplikasi kode dalam skala besar ! Mari kita jujur: Dalam sebagian besar proyek di 95% dari semua waktu yang benar-benar perlu dilakukan pengembang dengan pengecualian adalah untuk mengkomunikasikannya kepada pengguna (dan, dalam beberapa kasus, ke tim pengembangan juga, misalnya dengan mengirim e -mail dengan jejak stack). Jadi biasanya baris / blok kode yang sama digunakan di setiap tempat pengecualian ditangani.
Mari kita asumsikan bahwa kita melakukan pendataan sederhana di setiap blok tangkapan untuk beberapa jenis pengecualian yang diperiksa:
Jika itu pengecualian umum mungkin bahkan ada beberapa ratus blok uji coba seperti itu dalam basis kode yang lebih besar. Sekarang mari kita asumsikan bahwa kita perlu memperkenalkan penanganan pengecualian berbasis popup alih-alih konsol logging atau mulai mengirim e-mail tambahan ke tim pengembangan.
Tunggu sebentar ... apakah kita benar-benar akan mengedit semua itu beberapa ratus lokasi dalam kode ?! Anda mendapatkan poin saya :-).
Solusinya
Apa yang kami lakukan untuk mengatasi masalah itu adalah memperkenalkan konsep penangan pengecualian (yang akan saya sebut sebagai EH) untuk memusatkan penanganan pengecualian. Untuk setiap kelas yang perlu menangani pengecualian, instance handler pengecualian disuntikkan oleh kerangka kerja Ketergantungan kami . Jadi pola khas penanganan pengecualian sekarang terlihat seperti ini:
Sekarang untuk menyesuaikan penanganan pengecualian kami, kami hanya perlu mengubah kode di satu tempat (kode EH).
Tentu saja untuk kasus yang lebih kompleks, kami dapat menerapkan beberapa subkelas EH dan fitur pengungkit yang diberikan kerangka kerja DI kepada kami. Dengan mengubah konfigurasi kerangka DI kita, kita dapat dengan mudah mengalihkan implementasi EH secara global atau memberikan implementasi spesifik EH ke kelas dengan kebutuhan penanganan pengecualian khusus (misalnya menggunakan anotasi Guice @Named).
Dengan begitu kita dapat membedakan perilaku penanganan pengecualian dalam pengembangan dan merilis versi aplikasi (mis. Pengembangan - mencatat kesalahan dan menghentikan aplikasi, menambah kesalahan dengan lebih detail dan membiarkan aplikasi melanjutkan eksekusi) tanpa usaha.
Satu hal terakhir
Terakhir, namun tidak kalah pentingnya, tampaknya sentralisasi yang sama dapat diperoleh dengan hanya melewatkan pengecualian "naik" sampai mereka tiba di beberapa kelas penanganan pengecualian tingkat atas. Tapi itu mengarah pada kekacauan kode dan tanda tangan metode kami dan memperkenalkan masalah pemeliharaan yang disebutkan oleh orang lain di utas ini.
sumber
catch
blok dalam proyek nyata ini melakukan sesuatu yang benar-benar "berguna" dengan pengecualian? 10% akan bagus. Masalah biasa yang menghasilkan pengecualian seperti mencoba membaca konfigurasi dari file yang tidak ada, OutOfMemoryErrors, NullPointerExceptions, kesalahan integritas kendala basis data dll, dll. Apakah Anda benar-benar berusaha memulihkan dari semua file tersebut? Saya tidak percaya kamu :). Seringkali tidak ada cara untuk pulih.Anders berbicara tentang perangkap pengecualian yang dicek dan mengapa dia meninggalkannya dari C # dalam episode 97 radio Rekayasa Perangkat Lunak.
sumber
Langgan saya di c2.com sebagian besar masih tidak berubah dari bentuk aslinya: CheckedExceptionsAreIncompatiblewithVisitorPattern
Singkatnya:
Pola Pengunjung dan kerabatnya adalah kelas antarmuka di mana penelepon tidak langsung dan implementasi antarmuka keduanya tahu tentang pengecualian tetapi antarmuka dan penelepon langsung membentuk perpustakaan yang tidak bisa tahu.
Asumsi mendasar dari CheckedExceptions adalah semua pengecualian yang dinyatakan dapat dilemparkan dari titik mana pun yang memanggil metode dengan deklarasi itu. VisitorPattern mengungkapkan anggapan ini salah.
Hasil akhir dari pengecualian yang diperiksa dalam kasus-kasus seperti ini adalah banyak kode yang tidak berguna yang pada dasarnya menghapus kendala pengecualian yang diperiksa kompilator saat runtime.
Adapun masalah yang mendasarinya:
Gagasan umum saya adalah penangan tingkat atas perlu menafsirkan pengecualian dan menampilkan pesan kesalahan yang sesuai. Saya hampir selalu melihat salah satu pengecualian IO, pengecualian komunikasi (untuk beberapa alasan API membedakan), atau kesalahan fatal (bug program atau masalah parah pada server pendukung), jadi ini seharusnya tidak terlalu sulit jika kita mengizinkan jejak stack untuk yang parah masalah server.
sumber
Untuk mencoba menjawab pertanyaan yang tidak dijawab:
Pertanyaannya berisi IMHO penalaran bermata-mata. Hanya karena API memberi tahu Anda apa yang dilemparnya tidak berarti Anda menghadapinya dengan cara yang sama dalam semua kasus. Dengan kata lain, pengecualian yang perlu Anda tangkap bervariasi tergantung pada konteks di mana Anda menggunakan komponen yang melempar pengecualian.
Sebagai contoh:
Jika saya menulis tester koneksi untuk database, atau sesuatu untuk memeriksa validitas pengguna memasuki XPath, maka saya mungkin ingin menangkap dan melaporkan semua pengecualian yang dicentang dan tidak dicentang yang dilemparkan oleh operasi.
Namun, jika saya sedang menulis mesin pengolah, saya kemungkinan akan memperlakukan XPathException (dicentang) dengan cara yang sama seperti NPE: Saya akan membiarkannya naik ke atas utas pekerja, lewati sisa batch itu, log masalah (atau kirimkan ke departemen dukungan untuk diagnosis) dan tinggalkan umpan balik bagi pengguna untuk menghubungi dukungan.
sumber
Artikel ini adalah bagian terbaik dari teks tentang penanganan pengecualian di Jawa yang pernah saya baca.
Ia lebih memilih tidak terkecuali pada pengecekan terkecuali, tetapi pilihan ini dijelaskan dengan sangat baik dan berdasarkan pada argumen yang kuat.
Saya tidak ingin mengutip terlalu banyak konten artikel di sini (yang terbaik adalah membacanya secara keseluruhan) tetapi mencakup sebagian besar argumen pendukung pengecualian yang tidak diperiksa dari utas ini. Terutama argumen ini (yang tampaknya cukup populer) dibahas:
Penulis "tanggapan":
Dan pemikiran atau artikel utamanya adalah:
Jadi jika " tidak ada yang tahu bahwa kesalahan ini mungkin terjadi " ada sesuatu yang salah dengan proyek itu. Pengecualian seperti itu harus ditangani oleh setidaknya penangan pengecualian yang paling umum (mis. Yang menangani semua Pengecualian yang tidak ditangani oleh penangan yang lebih spesifik) seperti yang disarankan penulis.
Sedih sekali tidak banyak orang yang menemukan artikel hebat ini :-(. Saya merekomendasikan dengan sepenuh hati semua orang yang ragu pendekatan mana yang lebih baik untuk mengambil waktu dan membacanya.
sumber
Pengecualian yang diperiksa adalah, dalam bentuk aslinya, upaya untuk menangani kemungkinan daripada kegagalan. Tujuan terpuji adalah untuk menyoroti titik-titik tertentu yang dapat diprediksi (tidak dapat terhubung, file tidak ditemukan, dll) & memastikan pengembang menangani ini.
Apa yang tidak pernah dimasukkan dalam konsep asli, adalah untuk memaksa berbagai kegagalan sistemik & tidak dapat dipulihkan untuk diumumkan. Kegagalan ini tidak pernah benar untuk dinyatakan sebagai pengecualian yang diperiksa.
Kegagalan umumnya dimungkinkan dalam kode, dan kontainer EJB, web & Swing / AWT sudah memenuhi ini dengan menyediakan penangan pengecualian "permintaan gagal" terluar. Strategi paling dasar yang benar adalah untuk mengembalikan transaksi & mengembalikan kesalahan.
Satu poin penting, adalah bahwa runtime & pengecekan pengecualian secara fungsional setara.Tidak ada penanganan atau pemulihan yang dapat mengecek pengecualian, bahwa pengecualian runtime tidak bisa.
Argumen terbesar terhadap pengecualian “dicentang” adalah bahwa sebagian besar pengecualian tidak dapat diperbaiki. Fakta sederhananya adalah, kita tidak memiliki kode / subsistem yang rusak. Kami tidak dapat melihat implementasinya, kami tidak bertanggung jawab untuk itu, dan tidak bisa memperbaikinya.
Jika aplikasi kita bukan DB .. kita seharusnya tidak mencoba dan memperbaiki DB. Itu akan melanggar prinsip enkapsulasi .
Terutama bermasalah adalah bidang JDBC (SQLException) dan RMI untuk EJB (RemoteException). Alih-alih mengidentifikasi kontinjensi yang dapat diperbaiki sesuai dengan konsep "pengecualian yang diperiksa" yang asli, masalah keandalan sistemik yang meresap ini, yang sebenarnya tidak dapat diperbaiki, harus dinyatakan secara luas.
Kelemahan parah lain dalam desain Java, adalah bahwa penanganan pengecualian harus ditempatkan dengan benar pada tingkat "bisnis" atau "permintaan" setinggi mungkin. Prinsipnya di sini adalah "melempar lebih awal, terlambat". Pengecualian diperiksa tidak banyak tetapi menghalangi ini.
Kami memiliki masalah nyata di Jawa yang membutuhkan ribuan blok coba-coba-coba-lakukan, dengan proporsi yang signifikan (40% +) yang salah kode. Hampir tidak ada yang menerapkan penanganan atau keandalan yang asli, tetapi memaksakan overhead pengkodean utama.
Terakhir, "pengecualian yang diperiksa" cukup tidak kompatibel dengan pemrograman fungsional FP.
Desakan mereka pada "segera menangani" bertentangan dengan praktik terbaik penanganan pengecualian "catch late", dan setiap struktur FP yang abstrak loop / atau aliran kontrol.
Banyak orang berbicara tentang "menangani" pengecualian yang diperiksa, tetapi berbicara melalui topi mereka. Melanjutkan setelah kegagalan dengan data nol, tidak lengkap, atau salah untuk berpura - pura sukses tidak menangani apa pun. Ini rekayasa / malpraktek keandalan bentuk terendah.
Gagal bersih, adalah strategi paling dasar yang benar untuk menangani pengecualian. Meluruskan kembali transaksi, mencatat kesalahan & melaporkan respons "kegagalan" kepada pengguna adalah praktik yang baik - dan yang paling penting, mencegah kesalahan data bisnis yang dilakukan ke basis data.
Strategi lain untuk penanganan pengecualian adalah "coba lagi", "sambungkan kembali" atau "lewati", di tingkat bisnis, subsistem, atau permintaan. Semua ini adalah strategi keandalan umum, dan bekerja dengan baik / lebih baik dengan pengecualian runtime.
Terakhir, jauh lebih baik gagal, daripada dijalankan dengan data yang salah. Melanjutkan akan menyebabkan kesalahan sekunder, jauh dari penyebab awal & lebih sulit untuk di-debug; atau pada akhirnya akan menghasilkan data yang salah dilakukan. Orang dipecat karena itu.
Lihat:
- http://literatejava.com/exceptions/checked-exceptions-javas-biggest-mistake/
sumber
Seperti yang telah dinyatakan orang, pengecualian yang diperiksa tidak ada dalam bytecode Java. Mereka hanyalah mekanisme kompiler, tidak seperti pemeriksaan sintaksis lainnya. Aku lihat pengecualian diperiksa banyak seperti saya melihat compiler mengeluh tentang bersyarat berlebihan:
if(true) { a; } b;
. Itu membantu tapi saya mungkin sengaja melakukan ini, jadi biarkan saya mengabaikan peringatan Anda.Faktanya adalah, Anda tidak akan bisa memaksa setiap programmer untuk "melakukan hal yang benar" jika Anda menerapkan pengecualian yang dicek dan semua orang sekarang kerusakan kolateral yang hanya membenci Anda karena aturan yang Anda buat.
Perbaiki program yang buruk di luar sana! Jangan mencoba memperbaiki bahasa untuk tidak mengizinkannya! Bagi kebanyakan orang, "melakukan sesuatu tentang pengecualian" sebenarnya hanya memberi tahu pengguna tentang hal itu. Saya dapat memberi tahu pengguna tentang pengecualian yang tidak dicentang juga, jadi jauhkan kelas pengecualian Anda dari API saya.
sumber
Masalah dengan pengecualian yang diperiksa adalah bahwa pengecualian sering dilampirkan pada metode antarmuka jika bahkan satu implementasi dari antarmuka itu menggunakannya.
Masalah lain dengan pengecualian yang diperiksa adalah bahwa mereka cenderung disalahgunakan. Contoh sempurna dari ini di
java.sql.Connection
'sclose()
metode. Itu bisa melemparSQLException
, meskipun Anda sudah secara eksplisit menyatakan bahwa Anda sudah selesai dengan Koneksi. Informasi apa yang bisa ditutup () mungkin menyampaikan bahwa Anda peduli?Biasanya, ketika saya menutup () koneksi
*
, tampilannya seperti ini:Juga, jangan mulai dengan berbagai metode parse dan NumberFormatException ... .NET's TryParse, yang tidak melempar pengecualian, jauh lebih mudah untuk digunakan karena harus kembali ke Jawa (kami menggunakan Java dan C # tempat saya bekerja).
*
Sebagai komentar tambahan, Sambungan PooledConnection.close () bahkan tidak menutup koneksi, tetapi Anda masih harus menangkap SQLException karena itu menjadi pengecualian yang diperiksa.sumber
Programmer perlu mengetahui semua pengecualian yang dilemparkan suatu metode, agar dapat menggunakannya dengan benar. Jadi, mengalahkannya hanya dengan beberapa pengecualian tidak selalu membantu programmer yang ceroboh menghindari kesalahan.
Manfaat rampingnya melebihi biaya yang membebani (terutama dalam basis kode yang lebih besar dan kurang fleksibel di mana terus-menerus memodifikasi tanda tangan antarmuka tidak praktis).
Analisis statis dapat menjadi hal yang bagus, tetapi analisis statis yang benar-benar andal seringkali tidak fleksibel menuntut kerja keras dari programmer. Ada perhitungan biaya-manfaat, dan bilah perlu ditetapkan tinggi untuk cek yang mengarah ke kesalahan waktu kompilasi. Akan lebih bermanfaat jika IDE mengambil peran untuk mengkomunikasikan pengecualian yang mungkin dilemparkan metode (termasuk yang tidak dapat dihindari). Meskipun mungkin tidak akan dapat diandalkan tanpa deklarasi pengecualian yang dipaksakan, sebagian besar pengecualian masih akan dideklarasikan dalam dokumentasi, dan keandalan peringatan IDE tidak begitu penting.
sumber
Saya pikir ini adalah pertanyaan yang sangat bagus dan sama sekali tidak argumentatif. Saya pikir perpustakaan pihak ke-3 harus (secara umum) membuang pengecualian yang tidak dicentang . Ini berarti bahwa Anda dapat mengisolasi dependensi Anda pada perpustakaan (yaitu Anda tidak harus melemparkan kembali pengecualian atau melempar
Exception
- biasanya praktik buruk). Lapisan DAO Spring adalah contoh yang sangat baik untuk ini.Di sisi lain, pengecualian dari inti API Java harus di be umumnya diperiksa jika mereka bisa pernah ditangani. Ambil
FileNotFoundException
atau (favorit saya)InterruptedException
. Kondisi-kondisi ini hampir selalu harus ditangani secara khusus (yaitu reaksi Anda terhadap suatuInterruptedException
tidak sama dengan reaksi Anda terhadap suatuIllegalArgumentException
). Fakta bahwa pengecualian Anda diperiksa memaksa pengembang untuk memikirkan apakah suatu kondisi dapat ditangani atau tidak. (Yang mengatakan, aku jarang terlihatInterruptedException
ditangani dengan benar!)Satu hal lagi - a
RuntimeException
tidak selalu "di mana pengembang mendapat kesalahan". Pengecualian argumen ilegal dilemparkan ketika Anda mencoba dan membuatenum
penggunaanvalueOf
dan tidak adaenum
nama itu. Ini belum tentu kesalahan oleh pengembang!sumber
enum
nama anggota, hanya karena mereka menggunakanenum
objek. Jadi nama yang salah hanya bisa datang dari luar, baik itu file impor atau apa pun. Salah satu cara yang mungkin untuk berurusan dengan nama-nama tersebut adalah memanggilMyEnum#valueOf
dan menangkap IAE. Cara lain adalah dengan menggunakan pra-diisiMap<String, MyEnum>
, tetapi ini adalah detail implementasi.Berikut ini satu argumen terhadap pengecualian yang diperiksa (dari joelonsoftware.com):
sumber
goto
kata kunci. Dengan loop Anda dapat melihat tanda kurung kurawal ataubreak
ataucontinue
kata kunci. Semuanya melompat ke titik dalam metode saat ini. Tetapi Anda tidak selalu dapat melihatthrow
, karena seringkali itu bukan dalam metode saat ini tetapi dalam metode lain yang disebutnya (mungkin secara tidak langsung.)Bukti bagus bahwa Pengecualian Tidak diperlukan adalah:
Saya senang jika java akan memberi saya pilihan apa yang harus digunakan, ketika bekerja dengan core libs, seperti I / O. Like menyediakan dua salinan dari kelas yang sama - satu dibungkus dengan RuntimeEception. Lalu kita bisa membandingkan apa yang akan digunakan orang . Namun, untuk saat ini, banyak orang akan lebih memilih kerangka kerja di atas pada java, atau bahasa yang berbeda. Seperti Scala, JRuby terserah. Banyak yang percaya bahwa SUN itu benar.
sumber
RuntimeException
dengan pengecualian dalam yang sesuai). Sangat disayangkan bahwa lebih singkat untuk memiliki metode luarthrows
pengecualian dari metode batin, daripada memilikinya membungkus pengecualian dari metode batin, ketika tindakan yang terakhir akan lebih sering benar.Satu hal penting yang tidak disebutkan adalah bagaimana hal itu mengganggu antarmuka dan ekspresi lambda.
Katakanlah Anda mendefinisikan a
MyAppException extends Exception
. Ini adalah pengecualian tingkat atas yang diwarisi oleh semua pengecualian yang dilemparkan oleh aplikasi Anda. Setiap metode menyatakanthrows MyAppException
mana yang sedikit nuissance, tetapi dapat dikelola. Exception handler mencatat pengecualian dan memberi tahu pengguna entah bagaimana.Semua tampak OK sampai Anda ingin mengimplementasikan beberapa antarmuka yang bukan milik Anda. Jelas itu tidak menyatakan niat untuk melempar
MyApException
, jadi kompiler tidak memungkinkan Anda untuk melemparkan pengecualian dari sana.Namun, jika pengecualian Anda meluas
RuntimeException
, tidak akan ada masalah dengan antarmuka. Anda dapat secara sukarela menyebutkan pengecualian di JavaDoc jika Anda mau. Tapi selain itu hanya diam-diam menggelegak melalui apa pun, untuk terjebak dalam lapisan penanganan pengecualian Anda.sumber
Kami telah melihat beberapa referensi ke kepala arsitek C #.
Berikut ini sudut pandang alternatif dari seorang pria Jawa tentang kapan harus menggunakan pengecualian yang diperiksa. Dia mengakui banyak hal negatif yang disebutkan orang lain: Pengecualian Efektif
sumber
Saya sudah membaca banyak tentang penanganan pengecualian, bahkan jika (sebagian besar waktu) saya tidak bisa mengatakan saya senang atau sedih tentang keberadaan pengecualian yang diperiksa, inilah yang saya ambil: pengecualian yang diperiksa dalam kode tingkat rendah (IO, networking , OS, dll) dan pengecualian yang tidak dicentang di API tingkat tinggi / tingkat aplikasi.
Bahkan jika tidak ada begitu mudah untuk menarik garis di antara mereka, saya menemukan bahwa itu benar-benar menjengkelkan / sulit untuk mengintegrasikan beberapa API / perpustakaan di bawah atap yang sama tanpa membungkus semua waktu banyak pengecualian diperiksa tetapi di sisi lain, kadang-kadang berguna / lebih baik dipaksa untuk menangkap beberapa pengecualian dan memberikan yang berbeda yang lebih masuk akal dalam konteks saat ini.
Proyek yang saya kerjakan membutuhkan banyak perpustakaan dan mengintegrasikannya di bawah API yang sama, API yang sepenuhnya didasarkan pada pengecualian yang tidak dicentang. Kerangka kerja ini menyediakan API tingkat tinggi yang pada awalnya penuh dengan pengecualian yang diperiksa dan hanya beberapa yang tidak dicentang. pengecualian (Pengecualian Inisialisasi, ConfigurationException, dll) dan saya harus mengatakan itu tidak sangat ramah . Sebagian besar waktu Anda harus menangkap atau melempar pengecualian yang Anda tidak tahu bagaimana menangani, atau Anda bahkan tidak peduli (jangan bingung dengan Anda harus mengabaikan pengecualian), terutama di sisi klien di mana satu klik bisa melempar 10 kemungkinan (dicentang) pengecualian.
Versi saat ini (versi ke-3) hanya menggunakan pengecualian yang tidak dicentang, dan memiliki pengendali pengecualian global yang bertanggung jawab untuk menangani apa pun yang tidak tertangkap. API menyediakan cara untuk mendaftarkan penangan pengecualian, yang akan memutuskan apakah pengecualian dianggap sebagai kesalahan (sebagian besar waktu ini terjadi) yang berarti mencatat & memberi tahu seseorang, atau dapat berarti sesuatu yang lain - seperti pengecualian ini, AbortException, yang berarti memutus utas eksekusi saat ini dan jangan mencatat kesalahan apa pun karena memang diinginkan untuk tidak melakukannya. Tentu saja, untuk menyelesaikan semua utas khusus harus menangani metode run () dengan tangkapan {...} tangkapan (semua).
public void run () {
}
Ini tidak perlu jika Anda menggunakan WorkerService untuk menjadwalkan pekerjaan (Runnable, Callable, Worker), yang menangani segalanya untuk Anda.
Tentu saja ini hanya pendapat saya, dan itu mungkin bukan yang benar, tetapi sepertinya pendekatan yang baik untuk saya. Saya akan melihat setelah saya akan merilis proyek jika apa yang saya pikir itu baik untuk saya, itu baik untuk orang lain juga ... :)
sumber