Di banyak bahasa, blok try-catch adalah pernyataan (statement) yang tidak menghasilkan nilai. Kotlin memperlakukan try sebagai expression, sehingga kita bisa langsung mengikat hasilnya ke variabel. Pemulihan dari kegagalan pun jadi ringkas: nilai sukses dan nilai cadangan ditulis dalam satu ekspresi tanpa variabel sementara. Pada chapter ini kita akan menulis try sebagai expression, memadukannya dengan elvis operator, dan membuktikan bahwa blok finally tidak ikut menentukan nilai balik.
try sebagai Expression #
Bentuk paling ringkas dari try sebagai expression terlihat saat kita mengikat hasilnya langsung ke val. Bagian try menghasilkan nilai bila tidak ada exception; bagian catch menghasilkan nilai bila exception tertangkap. Tipe data akhir adalah common supertype dari semua cabang.
fun parseAngka(teks: String): Int {
val angka: Int = try {
teks.toInt()
} catch (e: NumberFormatException) {
0
}
return angka
}Tanpa sifat expression, kode di atas akan membutuhkan variabel var angka: Int = 0 yang diisi pada try lalu ditimpa pada catch. Sifat expression menyingkirkan mutasi tersebut dan membuat niat kode jelas pada satu tempat.
Mengikat Hasil ke Variabel #
Contoh berikut menunjukkan try sebagai expression di beberapa bentuk sekaligus. Kita akan mengikat hasilnya ke val, memadukan dengan elvis operator, dan menyusun try di dalam when.
// Demo try sebagai expression: mengembalikan nilai, finally tidak mengubah nilai balik.
fun parseAngka(teks: String): Int {
val angka: Int = try {
teks.toInt()
} catch (e: NumberFormatException) {
0
}
return angka
}
fun parseDenganElvis(teks: String?): Int {
// try-expression dipadukan dengan elvis operator
val angka: Int? = try {
teks?.toInt()
} catch (e: NumberFormatException) {
null
}
return angka ?: -1
}
fun konfigurasiSumber(sumber: String): String {
// try-expression di dalam when-expression
return when (sumber) {
"env" -> try { bacaDariEnv() } catch (e: IllegalStateException) { "default-env" }
"file" -> try { bacaDariFile() } catch (e: IllegalStateException) { "default-file" }
else -> "tidak dikenal"
}
}
fun bacaDariEnv(): String {
// simulasi: lempar bila environment variable kosong
throw IllegalStateException("ENV belum diatur")
}
fun bacaDariFile(): String {
return "nilai-dari-file"
}
fun demoFinallyTidakMengubahNilai(): Int {
// finally jalan setelah try, namun nilai balik try tidak berubah.
val hasil = try {
42
} catch (e: Exception) {
-1
} finally {
// statement di sini hanya efek samping, tidak mengubah `hasil`
println("[finally] jalan setelah try-expression, hasil tetap")
}
return hasil
}
fun main() {
println("=== parseAngka ===")
for (t in listOf("3", "99", "abc", "")) {
println("parseAngka(\"$t\") = ${parseAngka(t)}")
}
println()
println("=== parseDenganElvis ===")
for (t in listOf("7", "xyz", null)) {
println("parseDenganElvis(\"$t\") = ${parseDenganElvis(t)}")
}
println()
println("=== try-expression di dalam when ===")
for (sumber in listOf("env", "file", "cache")) {
println("sumber=$sumber -> ${konfigurasiSumber(sumber)}")
}
println()
println("=== finally tidak mengubah nilai try-expression ===")
val x = demoFinallyTidakMengubahNilai()
println("Nilai yang dikembalikan = $x")
}Output yang Dihasilkan:
=== parseAngka ===
parseAngka("3") = 3
parseAngka("99") = 99
parseAngka("abc") = 0
parseAngka("") = 0
=== parseDenganElvis ===
parseDenganElvis("7") = 7
parseDenganElvis("xyz") = -1
parseDenganElvis("null") = -1
=== try-expression di dalam when ===
sumber=env -> default-env
sumber=file -> nilai-dari-file
sumber=cache -> tidak dikenal
=== finally tidak mengubah nilai try-expression ===
[finally] jalan setelah try-expression, hasil tetap
Nilai yang dikembalikan = 42Mari kita bedah baris-baris penting dari kode di atas:
parseAngka:val angka: Int = try { ... } catch { ... }. CabangtrymenghasilkanIntdariteks.toInt(); cabangcatchmenghasilkanIntliteral0. Karena kedua cabang bertipeInt, variabelangkapun bertipeInt.parseDenganElvis: Cabangcatchmenghasilkannull, sehingga tipetrysecara keseluruhan menjadiInt?. Elvis operator?:kemudian menggantinulldengan-1, menghasilkanIntnon-null.konfigurasiSumber:try-expression dipakai sebagai cabangwhen. Setiap cabang memilih strategi pemulihan yang berbeda ("default-env"vs"default-file"), menunjukkan keterbacaan kode yang dipersingkat oleh sifat expression.demoFinallyTidakMengubahNilai: Blokfinallyhanya mencetak pesan. Nilai balik function tetap42(dari cabangtry) dan tidak diubah olehfinally.main: Loopformemakai string template$tuntuk menampilkan input dan hasil. Pendekatan ini memudahkan pemeriksaan visual saat membandingkan banyak kasus.
Tipe Hasil dan Common Supertype #
Bila cabang try dan catch menghasilkan tipe berbeda, kompilator akan memilih common supertype. Misalnya try menghasilkan String dan catch menghasilkan Int, maka tipe hasil try-expression adalah Any. Hal ini sering kali tidak diinginkan karena kita kehilangan informasi tipe.
fun ambilNilai(teks: String): Any {
return try {
teks.toInt()
} catch (e: NumberFormatException) {
"bukan angka"
}
}Pada contoh di atas, tipe hasil adalah Any (gabungan Int dan String). Pemanggil harus melakukan pengecekan tipe untuk memproses hasil lebih lanjut. Lebih baik menyamakan tipe kedua cabang, misalnya dengan membungkus hasil ke dalam sealed class Hasil atau dengan Result<Int> (lihat chapter Result Type Patterns).
finally Tidak Memengaruhi Nilai Balik #
Satu hal yang sering membingungkan: finally tidak berkontribusi pada nilai try-expression. Bahkan bila finally berisi statement return, nilainya tidak menjadi hasil try — return di finally justru akan menelan exception yang sedang naik dan menggantikan nilai balik function, sebuah kejadian yang biasanya tidak diinginkan.
fun hitungAman(a: Int, b: Int): Int {
val hasil = try {
a / b
} catch (e: ArithmeticException) {
0
} finally {
println("operasi $a / $b selesai")
}
return hasil * 2
}Pada function hitungAman, finally hanya mencatat log. Nilai hasil ditentukan sepenuhnya oleh cabang try atau catch. Pemisahan tanggung jawab ini membuat try-expression dapat dipakai sebagai ekspresi murni sementara finally hanya menjalankan efek samping.
Referensi Lanjutan #
- Exceptions
— bagian
trysebagai expression yang menghasilkan nilai
Penutup #
Dengan try sebagai expression, kode pemulihan kegagalan menjadi ringkas dan ekspresif: hasil sukses dan hasil cadangan ditulis pada satu tempat. Pemadukan dengan elvis operator dan when membuat pola seperti parse-or-default terbaca alami. Berikutnya, kita akan mendalami blok finally secara khusus, termasuk pola use {} untuk pembersihan resource otomatis.