Meja kerja pengembang Java dengan kode penanganan exception dan diagram hierarki Throwable di atas meja

Kapan Pakai Checked dan Unchecked Exception di Java

Perbedaan checked dan unchecked exception di Java terletak pada siapa yang dipaksa menangani kegagalan. Checked exception memaksa pemanggil menulis catch atau throws, sedangkan unchecked exception tidak. Pilihan ini memengaruhi bentuk tanda tangan method, jumlah try-catch, dan seberapa jujur sebuah API menyatakan mode kegagalannya.

Topik ini jarang selesai hanya dengan hafalan bahwa IOException itu checked dan NullPointerException unchecked. Yang lebih menentukan adalah siapa yang bisa memperbaiki masalah ketika kegagalan terjadi.

Dasar Pembeda di Level Bahasa

Compiler Java memeriksa hierarki Throwable. Semua turunan RuntimeException dan Error bebas dilempar tanpa deklarasi. Turunan lain dari Exception wajib dideklarasikan di klausa throws bila tidak ditangani di dalam method itu sendiri.

// Checked: pemanggil wajib menangani atau meneruskan
public String bacaKonfigurasi(Path path) throws IOException {
    return Files.readString(path);
}

// Unchecked: tidak wajib dideklarasikan
public int hitungRasio(int pembilang, int penyebut) {
    if (penyebut == 0) {
        throw new IllegalArgumentException("Penyebut tidak boleh nol");
    }
    return pembilang / penyebut;
}

Perbedaan mekanis itu terlihat sederhana, tetapi efeknya merambat ke desain lapisan di atasnya. Satu method yang mendeklarasikan checked exception memaksa seluruh rantai pemanggil ikut memutuskan apa yang dilakukan.

Kapan Checked Exception Masuk Akal

Checked exception cocok untuk kegagalan yang masih bisa ditangani pemanggil dengan cara berbeda, misalnya mencoba lokasi berkas lain, mengulang dengan kredensial berbeda, atau memberi tahu pengguna untuk mengisi ulang data. Kegagalan itu bagian dari kontrak normal sebuah operasi, bukan bug.

Contoh tipikal ada di lapisan I/O dan integrasi: berkas tidak ditemukan, koneksi database gagal dibuka, sertifikat tidak valid. Pemanggil punya jalan keluar konkret. Ketika jalan keluar itu benar-benar ada, checked exception memaksa pemanggil memikirkannya alih-alih membiarkan kegagalan lewat diam-diam.

Masalah muncul ketika checked exception dipakai untuk hal yang tidak bisa diperbaiki pemanggil, seperti kegagalan validasi internal atau konfigurasi yang salah sejak awal. Pemanggil akhirnya menulis catch kosong atau membungkusnya jadi RuntimeException hanya agar kode bisa dikompilasi. Pola itu menghapus manfaat utamanya.

Kapan Unchecked Exception Lebih Tepat

Unchecked exception pas untuk pelanggaran kontrak: argumen tidak valid, state objek tidak siap dipakai, atau invariant yang rusak. Ini kesalahan pemrograman, bukan kondisi yang diharapkan di alur normal. Pemanggil tidak bisa memperbaikinya saat itu juga selain memperbaiki kode.

Sejumlah API modern bergerak ke arah ini. Spring, misalnya, banyak melempar turunan RuntimeException seperti IllegalArgumentException untuk input yang salah. Alasannya praktis: tanda tangan method tetap bersih dan lapisan atas tidak dipaksa menangani kegagalan yang bukan tanggung jawabnya.

Risiko unchecked exception adalah kegagalan bisa lolos tanpa terlihat sampai runtime. Karena itu dokumentasi Javadoc pada klausa @throws menjadi penting. Compiler tidak akan mengingatkan, jadi dokumen dan pengujian yang menutup celah itu.

Membandingkan Dampak pada Lapisan Aplikasi

Aspek Checked Unchecked
Deklarasi di method Wajib di throws Tidak wajib
Efek pada pemanggil Dipaksa menangani Bebas memilih
Cocok untuk Kegagalan yang bisa dipulihkan Bug dan pelanggaran kontrak
Risiko utama catch kosong, rantai throws panjang Kegagalan lolos tanpa terdeteksi
Kesan pada API Kontrak eksplisit Tanda tangan lebih ringkas

Di lapisan service, unchecked exception biasanya lebih nyaman karena transaksi dan validasi berjalan di dalam satu alur. Di lapisan akses data atau klien eksternal, checked exception masih masuk akal untuk kegagalan jaringan dan I/O. Kegagalan jaringan jarang selesai di satu method; timeout dan koneksi putus sering perlu diteruskan ke handler global. Membaca panduan konfigurasi connection pool di Java membantu memetakan di titik mana kegagalan koneksi sebaiknya dilempar dan di titik mana sebaiknya diubah menjadi pesan yang bisa dibaca pengguna.

Membungkus Exception Tanpa Menghilangkan Informasi

Konversi dari checked ke unchecked sering diperlukan di batas lapisan. Kuncinya adalah menyertakan penyebab asli agar jejak tumpukan tetap utuh.

try {
    data = klienEksternal.ambilData();
} catch (IOException e) {
    throw new DataTidakTersediaException("Gagal mengambil data dari layanan", e);
}

Melempar ulang tanpa cause memotong jejak dan membuat diagnosis jauh lebih lambat. Hal yang sama berlaku ketika menangkap exception lalu melempar exception baru dengan pesan generik. Simpan penyebabnya.

Alternatif lain adalah membuat exception domain sendiri yang memperluas RuntimeException, lalu menanganinya di satu tempat, misalnya filter atau @ControllerAdvice. Pendekatan ini menjaga kode bisnis tetap fokus dan memusatkan pemetaan error ke respons HTTP. Untuk aplikasi yang dibangun di atas servlet container, titik pusat penanganan ini biasanya berada di lapisan web, dan pola arsitektur API Java untuk layanan internal membahas pembagian tanggung jawab tersebut.

Pedoman Praktis Memilih Jenis Exception

  • Tanyakan apakah pemanggil punya tindakan konkret saat kegagalan terjadi. Jika ya, checked exception bisa dipertimbangkan.
  • Untuk validasi argumen dan pelanggaran invariant, pakai unchecked exception.
  • Hindari catch kosong. Jika terpaksa menangani, catat log atau lempar ulang dengan konteks.
  • Jangan menambah checked exception ke method publik hanya karena implementasi internal melemparnya. Bungkus di batas lapisan.
  • Dokumentasikan setiap unchecked exception yang mungkin keluar dari method publik melalui @throws.
  • Jaga jumlah jenis exception yang dideklarasikan tetap kecil. Terlalu banyak checked exception membuat pemanggil menulis blok penanganan yang seragam dan tidak berguna.

Keputusan yang diambil di awal akan terasa mahal untuk diubah nanti karena menyentuh banyak tanda tangan method. Karena itu, tetapkan konvensi per lapisan sebelum kode bertambah banyak. Lapisan data boleh memakai checked exception, lapisan service mengubahnya menjadi exception domain unchecked, dan lapisan web memetakan exception domain ke kode status HTTP.

Pada aplikasi yang berjalan di dalam container seperti Tomcat, penanganan error juga perlu mempertimbangkan siapa yang mencatat log. Melempar exception unchecked ke atas dan membiarkan handler global mencatatnya biasanya lebih rapi daripada mencatat di banyak titik. Untuk urusan deployment dan konfigurasi container, cara deploy aplikasi Java ke Tomcat langkah demi langkah memberi gambaran di mana halaman error dan log container diatur.

Jika kegagalan exception berkaitan dengan layanan lain yang tidak merespons, penyebabnya kadang berada di jaringan, bukan di kode. Menguji kualitas koneksi kantor secara berkala lewat panduan uji kualitas jaringan kantor dengan ping berkala membantu memisahkan masalah aplikasi dari masalah infrastruktur. Untuk tim yang juga mengelola perangkat mobile internal, prosedur mengunci aplikasi dengan sidik jari di HP Android relevan saat aplikasi Java dipasangkan dengan klien lapangan.

Ringkasan

Checked exception memaksa pemanggil mengakui kegagalan, sehingga cocok untuk kondisi yang masih bisa ditangani. Unchecked exception lebih pas untuk bug, pelanggaran kontrak, dan kegagalan yang diselesaikan di satu handler global. Yang menentukan adalah apakah pemanggil benar-benar punya pilihan saat kegagalan terjadi.

Pertanyaan yang Sering Diajukan

Apakah unchecked exception selalu lebih baik?

Tidak. Untuk operasi I/O dan integrasi yang kegagalannya bisa dipulihkan, checked exception memberi sinyal jelas kepada pemanggil. Masalahnya muncul ketika jenis ini dipakai untuk kondisi yang tidak bisa ditangani pemanggil.

Bagaimana cara mengubah checked menjadi unchecked tanpa kehilangan jejak error?

Bungkus checked exception dalam exception domain yang memperluas RuntimeException, lalu sertakan exception asli sebagai parameter kedua konstruktor. Penyebab tetap tercatat di jejak tumpukan.

Apakah finally cocok untuk menutup resource pada kedua jenis exception?

Bisa, tetapi blok try-with-resources lebih ringkas dan otomatis menutup resource di semua jalur, baik checked maupun unchecked. Pakai finally untuk hal lain yang harus dijalankan, seperti membersihkan state sementara.