Kotlin menyediakan lima scope functions bawaan: let, run, with, apply, dan also. Kelima function ini membantu mengelompokkan operasi pada objek yang sama, menangani null-safety, dan menyisipkan side effect tanpa merusak chain. Pada chapter ini kita akan melihat ringkasan kelima scope functions, perbandingan tegas antara receiver (this vs it) dan nilai kembalian (objek vs lambda result), serta panduan memilih function yang tepat untuk setiap situasi. Chapter ini menjadi pengantar untuk chapter-chapter khusus yang membahas setiap function lebih mendalam.

Apa Itu Scope Functions #

Scope functions adalah function bawaan standard library yang tujuan utamanya adalah membentuk scope sementara di mana sebuah objek tersedia tanpa prefix. Kelima function ini tidak melakukan hal yang tidak bisa kita lakukan tanpa mereka — manfaat utamanya adalah keterbacaan dan kekompakan kode.

kotlin Lima scope functions
val tugas = Tugas(1, "Belajar Kotlin")

val a = tugas.let { it.judul.uppercase() }              // returns lambda result
val b = tugas.run { "Tugas #$id: $judul" }              // returns lambda result
val c = with(tugas) { "[ID $id] $judul" }               // returns lambda result (not extension)
val d = tugas.apply { judul = "Belajar Lanjut" }        // returns the object
val e = tugas.also { println("Log: ${it.judul}") }      // returns the object

Tabel Perbandingan Scope Functions #

FunctionReceiverReturnBentukUse Case Utama
letitlambda resultextensionNull-safety, transformasi
runthislambda resultextensionEksekusi blok pada receiver
withthislambda resultfunction biasaMengelompokkan panggilan
applythisobjek (receiver)extensionKonfigurasi objek
alsoitobjek (receiver)extensionSide effect (log, debug)

Contoh Kelima Scope Functions #

Mari kita lihat semua scope functions dalam satu file yang dapat dijalankan, dengan chaining di akhir.

kotlin
// scope_functions.kt
// Lima scope functions: let, run, with, apply, also.

data class Tugas(val id: Int, var judul: String, var selesai: Boolean = false)

fun main() {
    val tugas = Tugas(1, "Belajar Kotlin")

    // let — receiver `it`, mengembalikan lambda result.
    val judulKapital = tugas.let { it.judul.uppercase() }
    println("let result: $judulKapital")

    // run — receiver `this`, mengembalikan lambda result.
    val ringkasan = tugas.run {
        "Tugas #$id: $judul (selesai=$selesai)"
    }
    println("run result: $ringkasan")

    // with — bukan extension, receiver `this`, mengembalikan lambda result.
    val cetakan = with(tugas) {
        "[ID $id] $judul"
    }
    println("with result: $cetakan")

    // apply — receiver `this`, mengembalikan objek.
    val dikonfigurasi = tugas.apply {
        judul = "Belajar Kotlin Lanjut"
        selesai = false
    }
    println("apply result: $dikonfigurasi")

    // also — receiver `it`, mengembalikan objek.
    val dilihat = tugas.also {
        println("  (mencatat tugas: ${it.judul})")
    }
    println("also result: $dilihat")

    // Chaining: apply untuk konfigurasi, lalu also untuk logging.
    val final = Tugas(2, "Draft")
        .apply {
            judul = "Belajar Scope Functions"
            selesai = true
        }
        .also { println("  Tugas siap: ${it.judul}") }
        .let { "${it.id}: ${it.judul}" }

    println("Hasil chaining: $final")
}

Output yang Dihasilkan:

text
let result: BELAJAR KOTLIN
run result: Tugas #1: Belajar Kotlin (selesai=false)
with result: [ID 1] Belajar Kotlin
apply result: Tugas(id=1, judul=Belajar Kotlin Lanjut, selesai=false)
  (mencatat tugas: Belajar Kotlin Lanjut)
also result: Tugas(id=1, judul=Belajar Kotlin Lanjut, selesai=false)
  Tugas siap: Belajar Scope Functions
Hasil chaining: 2: Belajar Scope Functions

Mari kita telaah baris-baris kode di atas:

  1. Baris 9 (tugas.let { it.judul.uppercase() }): let memberi akses ke objek via it (parameter implisit). Hasil block (String kapital) menjadi nilai kembalian. Cocok untuk transformasi nilai.
  2. Baris 13 (tugas.run { "Tugas #$id: $judul (selesai=$selesai)" }): run memberi akses ke objek via this. Karena this implisit, kita memanggil id, judul, selesai tanpa prefix. Hasil block (string ringkasan) menjadi nilai kembalian.
  3. Baris 19 (with(tugas) { "[ID $id] $judul" }): with bukan extension function — menerima objek sebagai parameter pertama. Di dalam block, this adalah objek. Hasil block menjadi nilai kembalian.
  4. Baris 25 (tugas.apply { judul = "Belajar Kotlin Lanjut"; selesai = false }): apply memberi akses via this. Di dalam block kita mengubah property objek. Mengembalikan objek itu sendiri (bukan hasil block). Cocok untuk konfigurasi.
  5. Baris 31 (tugas.also { println(" (mencatat tugas: ${it.judul})") }): also memberi akses via it. Block biasanya berisi side effect (log, debug). Mengembalikan objek itu sendiri — tidak terpengaruh oleh hasil block.
  6. Baris 38-45 (Tugas(2, "Draft").apply { ... }.also { ... }.let { ... }): chaining tiga scope functions. apply mengonfigurasi objek baru, also mencatat log, let mengubah menjadi string ringkasan. Hasil akhir adalah string.

Panduan Memilih Scope Function #

TujuanPilihAlasan
Null-safety (obj?.let { ... })letSafe-call + transformasi
Transformasi nilai ke tipe lainlet atau runKembalian lambda result
Konfigurasi objek (builder)applyKembalian objek
Side effect tanpa ganggu chainalsoKembalian objek, it eksplisit
Mengelompokkan beberapa panggilan pada objekwithSintaks function biasa, this
Eksekusi block pada receiver, ambil hasilrunExtension + lambda result

this vs it — Pilihan Stylistic #

Receiver this (apply, run, with) membuat kode di dalam block terbaca seperti method class — tanpa prefix. Receiver it (let, also) lebih eksplisit dan bisa diganti nama bila konteks memerlukan:

kotlin this vs it
// `this` — akses tanpa prefix
tugas.apply { judul = "Baru" }

// `it` — eksplisit, bisa rename
tugas.also { t -> println(t.judul) }

Referensi Lanjutan #

Penutup #

Kelima scope functions menyediakan alat ringkas untuk pola umum: konfigurasi objek, transformasi nilai, null-safety, dan side effect. Dengan mengingat tabel perbandingan receiver dan return value, kita dapat memilih function yang tepat untuk setiap situasi. Selanjutnya, kita akan mendalami setiap function satu per satu — mulai dari let yang paling sering dipakai untuk null-safety dan transformasi nilai.