Setelah memahami hierarki Throwable, kita siap menulis kode yang menangkap exception secara eksplisit. Mekanisme try-catch di Kotlin mirip dengan Java: kita bungkus kode berisiko dalam blok try, lalu sediakan satu atau lebih catch yang menangani tipe exception tertentu. Pada chapter ini kita akan mempraktikkan multiple catch clause, urutan penangkapan yang benar, dan blok finally yang bersifat opsional. Setelah ini, kita akan melihat bahwa try juga bisa berfungsi sebagai expression.
Bentuk Dasar try-catch #
Blok try-catch paling sederhana terdiri atas satu blok try dan satu catch. Kode di dalam try dijalankan sampai tuntas; jika exception dilempar, JVM mencari catch pertama yang tipe parameternya kompatibel dengan exception tersebut. Jika ketemu, blok catch dijalankan dan program melanjutkan eksekusi normal setelahnya.
fun parseAngka(teks: String): Int {
return try {
teks.toInt()
} catch (e: NumberFormatException) {
println("Gagal parse: ${e.message}")
0
}
}Kode di atas mengembalikan nilai yang dihasilkan blok try (lihat chapter try Expression untuk detail), atau 0 bila parsing gagal. Karena NumberFormatException adalah turunan RuntimeException, kompilator tidak memaksa pemanggil untuk menangkapnya. Kita memilih menangkap karena kita memiliki strategi pemulihan: kembalikan nilai default 0.
Multiple catch Clause #
Bila satu blok try berpotensi memunculkan beberapa tipe exception berbeda, kita dapat menambahkan beberapa catch. Setiap catch menangani satu tipe, dan Kotlin akan memilih catch pertama yang kompatibel. Bayangkan catch sebagai percabangan tipe: urutan sangat berpengaruh.
// Demo try-catch dengan multiple catch clause, urutan spesifik ke umum, dan finally.
import java.io.IOException
fun bacaVersi(teks: String): Int {
if (teks.isEmpty()) {
throw IOException("teks versi kosong")
}
return teks.toInt()
}
fun prosesInput(teks: String): String {
try {
val versi = bacaVersi(teks)
return "versi $versi OK"
} catch (e: NumberFormatException) {
// catch paling spesifik dulu
return "Format angka salah: ${e.message}"
} catch (e: IOException) {
// catch yang lebih umum setelahnya
return "Kesalahan I/O: ${e.message}"
} catch (e: Exception) {
// catch penampung umum terakhir
return "Kesalahan umum: ${e::class.simpleName}"
} finally {
println("[finally] selesai memproses input \"$teks\"")
}
}
fun demoUrutanSalah(): String {
// JANGAN tulis seperti ini: catch Exception di depan menelan exception spesifik.
// Kompilator Kotlin menolak kode ini, jadi kita tampilkan catatan saja.
return "Catch Exception di depan akan menutupi NumberFormatException; " +
"kompilator Kotlin menolak urutan tersebut."
}
fun main() {
println("=== Beberapa Input ===")
val inputDaftar = listOf("3", "12", "abc", "", "true")
for (input in inputDaftar) {
val hasil = prosesInput(input)
println("input=\"$input\" -> $hasil")
}
println()
println("=== Catatan urutan ===")
println(demoUrutanSalah())
println()
println("=== Try dengan finally saja (tanpa catch) ===")
try {
try {
val data = intArrayOf(1, 2, 3)
println("elemen index 5 = ${data[5]}")
} finally {
println("[finally] blok finally tanpa catch tetap jalan")
}
} catch (e: ArrayIndexOutOfBoundsException) {
println("[luar] menangkap: ${e::class.simpleName}")
}
}Output yang Dihasilkan:
=== Beberapa Input ===
[finally] selesai memproses input "3"
input="3" -> versi 3 OK
[finally] selesai memproses input "12"
input="12" -> versi 12 OK
[finally] selesai memproses input "abc"
input="abc" -> Format angka salah: For input string: "abc"
[finally] selesai memproses input ""
input="" -> Kesalahan I/O: teks versi kosong
[finally] selesai memproses input "true"
input="true" -> Format angka salah: For input string: "true"
=== Catatan urutan ===
Catch Exception di depan akan menutupi NumberFormatException; kompilator Kotlin menolak urutan tersebut.
=== Try dengan finally saja (tanpa catch) ===
[finally] blok finally tanpa catch tetap jalan
[luar] menangkap: ArrayIndexOutOfBoundsExceptionMari kita amati bagian-bagian penting dari contoh di atas:
bacaVersi: Memutuskan jenis exception berdasarkan kondisi input. String kosong memunculkanIOException; string non-angka memunculkanNumberFormatExceptiondaritoInt(). Dengan demikian, satu function bisa menghasilkan dua tipe kegagalan berbeda.prosesInput: BloktrymemanggilbacaVersi. Tigacatchdisusun dari paling spesifik (NumberFormatException) ke paling umum (Exception). Setiap cabang menghasilkan pesan dengan konteks berbeda.- Blok
finally: Selalu dijalankan baik saat sukses maupun saat exception. Pada contoh,finallymencatat setiap input yang selesai diproses, terlepas dari hasilnya. demoUrutanSalah: Memberi catatan bahwa menempatkancatch (e: Exception)di paling depan akan menelan exception spesifik. Kompilator Kotlin secara aktif menolak urutan tersebut dengan pesan catch parameter is already caught.- Try dengan
finallysaja: Bentuktry-finallytanpacatchvalid. Exception akan tetap naik ke pemanggil setelahfinallyselesai, terbukti dari bloktryluar yang menangkapArrayIndexOutOfBoundsException.
Urutan: Spesifik ke Umum #
Aturan urutan catch di Kotlin mengikuti prinsip tipe data: catch yang mendeklarasikan subkelas harus muncul sebelum catch yang mendeklarasikan superclass. Bila kita menulis catch (e: Exception) di depan, semua exception turunan akan masuk ke cabang tersebut, dan catch spesifik di bawahnya tidak akan pernah dijalankan. Kompilator Kotlin mendeteksi kondisi ini dan menolak kompilasi.
✅ Urutan benar ❌ Urutan ditolak
catch NumberFormatException catch Exception
catch IOException catch NumberFormatException
catch Exception catch IOExceptionPedoman praktis: urutkan catch dari tipe yang paling bawah di hierarki (paling spesifik) menuju tipe yang paling atas (paling umum). Bila ragu, bayangkan branch table: setiap exception hanya boleh masuk ke satu cabang.
finally yang Opsional #
Blok finally tidak wajib. Kita boleh menulis try-catch tanpa finally, atau try-finally tanpa catch. Blok finally hadir ketika kita perlu menjamin sebuah aksi pembersihan selalu terjadi, baik saat try sukses maupun saat catch menangkap exception. Penggunaan paling umum adalah menutup resource seperti file, koneksi database, atau socket jaringan.
Pada chapter finally Block kita akan mendalami penggunaan finally untuk pembersihan resource dan use {} sebagai ganti try-with-resources warisan Java.
Referensi Lanjutan #
- Exceptions
— panduan resmi blok
try-catchdan urutan multiplecatch
Penutup #
Dengan multiple catch yang tersusun spesifik ke umum, kita dapat menangani beragam tipe kegagalan dalam satu blok try tanpa kehilangan ketepatan. Blok finally yang opsional memberi kita tempat pasti untuk pembersihan. Berikutnya, kita akan memanfaatkan fakta bahwa try sesungguhnya adalah expression — sebuah konstruksi yang menghasilkan nilai dan membuat kode pemulihan menjadi jauh lebih ringkas.