Try-Catch — konstruksi penangkapan eksepsi yang memungkinkan eksekusi kode yang berpotensi berbahaya dalam blok yang dilindungi dan menangani kesalahan dengan benar tanpa penghentian program secara tiba-tiba. Blok try berisi kode yang dapat melempar eksepsi, catch menangkapnya dan menjalankan logika pemulihan. Menurut Apple Swift Documentation (2026), blok finally dieksekusi terlepas dari apakah eksepsi dilemparkan atau tidak, menjamin pembebasan sumber daya.
Poin Utama
Try-Catch — konstruksi fundamental penanganan eksepsi terstruktur yang ada di sebagian besar bahasa pemrograman modern. Terdiri dari tiga blok: try (percobaan mengeksekusi kode berbahaya), catch (menangkap dan menangani eksepsi) dan finally opsional (finalisasi). Ide konstruksinya adalah memisahkan logika operasi bisnis dari logika penanganan kesalahan, membuat kode lebih mudah dibaca dan diprediksi.
Konsep ini pertama kali diimplementasikan dalam bahasa C++ sebagai try/catch, kemudian diadopsi oleh Java, C#, Swift, Kotlin, Dart, Python, JavaScript dan bahasa lainnya. Setiap bahasa menambahkan fiturnya sendiri: di Swift blok catch harus lengkap, di Kotlin try-catch bisa menjadi ekspresi (expression), di Dart finally wajib untuk sumber daya aliran. Terlepas dari perbedaannya, prinsip dasarnya sama: kesalahan ditangani sedekat mungkin dengan tempat terjadinya, bukan secara global.
Penggunaan Try-Catch sangat relevan dalam pengembangan mobile, di mana faktor eksternal — kehilangan jaringan, respons server yang salah, kekurangan memori — terus terjadi. Penanganan eksepsi yang benar mencegah crash aplikasi dan memastikan UX yang tepat: pengguna menerima pesan kesalahan alih-alih penutupan aplikasi secara tiba-tiba. Menurut Google Android Kotlin Style Guide (2026), setiap fungsi yang dapat melempar eksepsi harus menanganinya melalui try-catch atau mendeklarasikan throws di tanda tangan.
Mekanisme eksekusi try-catch didasarkan pada stack unwinding. Ketika di dalam blok try sebuah eksepsi dilemparkan melalui operator throw (atau sebagai akibat dari kesalahan sistem), aliran eksekusi normal segera dihentikan. Eksekusi berpindah ke tingkat yang lebih tinggi dalam tumpukan panggilan untuk mencari blok catch yang sesuai. Bahasa modern mencari catch dengan tipe yang sesuai dengan tipe eksepsi yang dilemparkan, menggunakan mekanisme pencocokan tipe (type matching).
Jika catch yang sesuai ditemukan, tubuhnya dieksekusi, setelah itu eksekusi berlanjut setelah seluruh konstruksi try-catch-finally. Jika catch tidak ditemukan, eksepsi naik lebih jauh dalam tumpukan dan dapat ditangani di tingkat yang lebih tinggi — hingga handler global yang dalam aplikasi mobile menampilkan dialog kesalahan kepada pengguna. Jika eksepsi tidak ditangani di mana pun, aplikasi akan crash. Karena itulah penanganan yang benar dari semua tipe eksepsi yang mungkin sangat penting untuk stabilitas aplikasi.
fun readUserData(): User {
return try {
val response = api.fetchUser()
parseUser(response)
} catch (e: IOException) {
logError("Kesalahan jaringan", e)
throw AppException("Gagal memuat data")
} catch (e: JsonParseException) {
logError("Kesalahan parsing", e)
return User.default()
} finally {
closeLoadingIndicator()
}
}
Kode pertama-tama mencoba mengeksekusi permintaan API dan mem-parse respons. Jika terjadi IOException (masalah jaringan), eksepsi dicatat dan diteruskan sebagai AppException. Jika JsonParseException — pengguna default dikembalikan. Blok finally secara terjamin menyembunyikan indikator pemuatan, mencegah kebocoran komponen UI di layar.
Di Swift, penanganan kesalahan diimplementasikan melalui protokol Error (sebelumnya ErrorType). Tipe apa pun yang sesuai dengan Error dapat dilemparkan melalui operator throw. Fungsi yang dapat melempar kesalahan ditandai dengan kata kunci throws di tanda tangan. Pemanggilan fungsi semacam itu memerlukan prefiks try (untuk try-catch eksplisit), try? (hasil opsional) atau try! (eksekusi paksa tanpa penanganan kesalahan).
enum NetworkError: Error {
case noConnection
case serverError(code: Int)
case timeout
}
func fetchUser(id: Int) throws -> User {
guard isConnected() else {
throw NetworkError.noConnection
}
let data = try performRequest(path: "/users/\(id)")
return try decodeUser(from: data)
}
do {
let user = try fetchUser(id: 42)
updateUI(user)
} catch NetworkError.noConnection {
showOfflineAlert()
} catch let error as NetworkError {
showError("Jaringan " + error.localizedDescription)
} catch {
showGenericError()
}
Enum NetworkError mengimplementasikan protokol Error, mendefinisikan tiga kasus: noConnection, serverError dengan kode dan timeout. Fungsi fetchUser ditandai dengan throws: pertama memeriksa koneksi, kemudian mengeksekusi permintaan dan parsing. Di blok do-catch, tiga catch menangani skenario berbeda: kasus spesifik noConnection, tipe umum NetworkError dan semua kesalahan lainnya. Ini memungkinkan menampilkan pesan yang berbeda kepada pengguna tergantung pada jenis masalah.
Kotlin mewarisi try-catch-finally dari Java, tetapi menambahkan perbedaan penting: di Kotlin, try-catch adalah sebuah ekspresi (expression), bukan pernyataan (statement). Ini berarti hasil dari blok try atau blok catch dapat ditetapkan ke variabel. Ekspresi terakhir di blok try menjadi hasil saat sukses, ekspresi terakhir di catch — saat kesalahan. Jika kesalahan tidak ditangani oleh catch mana pun, eksepsi diteruskan ke atas dalam tumpukan.
sealed class Result<out T> {
data class Success<out T>(val data: T) : Result<T>()
data class Error(val exception: Throwable) : Result<Nothing>()
}
fun loadData(): Result<List<Item>> {
return try {
val response = api.getItems()
Result.Success(response.toList())
} catch (e: HttpException) {
Log.e("HTTP ", e)
Result.Error(e)
} catch (e: IOException) {
Log.e("Jaringan ", e)
Result.Error(e)
}
}
Dalam contoh, sealed class Result membungkus respons sukses atau kesalahan. Fungsi loadData menggunakan try-catch sebagai ekspresi: saat sukses mengembalikan Result.Success, saat eksepsi HttpException atau IOException — Result.Error dengan pencatatan. Pendekatan ini memungkinkan pihak pemanggil menangani kesalahan tanpa eksepsi — melalui ekspresi when berdasarkan tipe Result. Ini sangat nyaman di Jetpack Compose untuk menampilkan berbagai status UI (Loading, Success, Error) melalui StateFlow dan collectAsState.
Dart mendukung try-catch-finally dengan sintaks yang mirip dengan Java, tetapi dengan tambahan klausa on untuk memfilter berdasarkan tipe eksepsi tanpa menentukan variabel. Ini nyaman ketika eksepsi itu sendiri tidak diperlukan — hanya fakta tipenya yang penting. Dart juga mendukung blok catch dengan dua parameter: objek eksepsi dan StackTrace, yang berguna untuk mencatat seluruh rantai panggilan.
import 'dart:io';
import 'dart:convert';
class UserRepository {
Future<User> fetchUser(String id) async {
try {
final client = HttpClient();
final request = await client.getUrl(
Uri.parse('https://api.example.com/users/$id')
);
final response = await request.close();
final body = await response.transform(utf8.decoder).join();
return User.fromJson(json.decode(body));
} on SocketException catch (e, stackTrace) {
log("No internet", e, stackTrace);
throw AppException("Connection failed");
} on FormatException {
throw AppException("Invalid response format");
} finally {
client.close();
}
}
}
SocketException ditangkap dengan objek dan StackTrace untuk pencatatan detail, kemudian diteruskan sebagai AppException. FormatException ditangkap tanpa variabel — cukup mengetahui bahwa format respons tidak benar. Blok finally secara terjamin menutup HttpClient, mencegah kebocoran soket. Di Flutter, pendekatan ini sangat penting untuk pengujian Widget, di mana eksepsi yang tidak ditangani di State.initState menyebabkan kegagalan seluruh sesi pengujian.
Bahkan pengembang berpengalaman pun membuat kesalahan saat bekerja dengan try-catch yang menyebabkan kebocoran memori, bug tersembunyi atau perilaku aplikasi yang tidak tepat. Mari kita lihat lima masalah paling umum dalam pengembangan mobile.
Catch kosong — salah satu praktik terburuk. Eksepsi ditelan, aplikasi terus berjalan dalam kondisi tidak benar, dan pengembang tidak mengetahui masalahnya. Selalu setidaknya catat eksepsi. Di Kotlin gunakan catch(e: Exception) { Log.e(...) }, di Swift — catch { print($0) }. Di Dart, catch minimal yang dapat diterima harus memanggil debugPrint atau menulis ke Crashlytics.
Menangkap semua eksepsi melalui catch (Exception e) tanpa membedakan berdasarkan tipe menyembunyikan kesalahan tak terduga — NullPointerException, OutOfMemoryError, StackOverflowError. Tangkap hanya tipe-tipe yang Anda harapkan dan dapat Anda tangani. Untuk sisanya, biarkan propagasi ke atas. Dalam pengembangan mobile, catch spesifik untuk IOException, TimeoutException, AuthException memberikan pesan yang lebih bermakna kepada pengguna.
Sumber daya — file, soket, kursor DB, animasi — harus dibebaskan di finally atau di blok use (AutoCloseable). Pengembang sering lupa menutup sumber daya saat eksepsi, yang menyebabkan kebocoran. Di Kotlin gunakan .use { } untuk sumber daya Closeable, di Swift — defer { }, di Dart — await using dari paket async. Blok finally menjamin pembebasan bahkan saat pelemparan eksepsi di dalam catch.
Dalam kode asinkron, try-catch tidak menangkap eksepsi dari thread lain. Di Kotlin Coroutines gunakan CoroutineExceptionHandler atau SupervisorJob. Di Swift async/await — do-catch di dalam Task. Di Flutter — runZonedGuarded untuk penangkapan global. Mengabaikan aturan ini adalah penyebab crash yang sulit direproduksi di produksi.
Penanganan eksepsi tidak boleh memblokir antarmuka pengguna untuk waktu yang tidak ditentukan. Tampilkan pesan spesifik kepada pengguna dan berikan kemungkinan untuk mengulangi operasi. Snackbar dengan tombol Retry di Kotlin/Compose, UIAlertController dengan action di Swift, SnackBar dengan action di Flutter — UX minimal yang cukup untuk kesalahan jaringan atau server. Hindari dialog umum „Terjadi kesalahan" tanpa kemungkinan pemulihan.
Pertanyaan yang Sering Diajukan
Try-catch menggunakan eksepsi dan stack unwinding untuk penanganan kesalahan, yang bisa mahal dari segi kinerja saat jumlah kesalahan besar. Result Type adalah tipe-kontainer (Success atau Failure) yang ditangani melalui pattern matching tanpa stack unwinding, lebih efisien untuk kesalahan yang diharapkan.
Finally wajib jika blok try membuka sumber daya (file, soket, kursor) yang perlu ditutup. Jika sumber daya tidak dibuka, finally tidak diperlukan. Di bahasa modern gunakan AutoCloseable/use/defer untuk penutupan sumber daya otomatis tanpa finally. Blok use di Kotlin dan Swift menggantikan finally untuk objek Closeable.
Dalam aliran normal (tanpa eksepsi), try-catch praktis tidak memengaruhi kinerja — JVM dan kompiler Swift mengoptimalkan kasus ini. Tetapi saat pelemparan eksepsi, terjadi stack unwinding yang bisa memakan waktu 10–100 µs tergantung kedalaman tumpukan. Jangan gunakan eksepsi untuk kontrol aliran eksekusi — ini adalah antipola.
Di coroutine gunakan try-catch di dalam coroutineScope atau CoroutineExceptionHandler untuk penangkapan global. SupervisorJob mencegah pembatalan coroutine induk saat kesalahan di coroutine anak. Untuk launch gunakan CoroutineExceptionHandler, untuk async — try-catch di sekitar await().
Banyak catch lebih disukai: kode dibaca secara linear, setiap blok menangani satu tipe eksepsi. Satu catch dengan if-else lebih sulit dipelihara, mudah melewatkan tipe eksepsi baru. Di Swift, banyak catch wajib untuk penanganan lengkap enum Error, di Kotlin tidak ada batasan, tetapi praktik terbaik adalah catch terpisah per tipe.
Ringkasan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga