run adalah scope function yang menggunakan this sebagai receiver dan mengembalikan hasil block sebagai nilai kembalian. Berbeda dari let yang memakai it, run membuat kode di dalam block terbaca seperti method class — panggilan member tanpa prefix. Pada chapter ini kita akan melihat empat pola run — eksekusi block dengan konfigurasi dan hasil, run tanpa receiver sebagai blok lokal, run pada nullable dengan safe-call, serta perbandingan dengan with.

Apa Itu run #

Signature run:

kotlin Signature run
// Extension pada T
inline fun <T, R> T.run(block: T.() -> R): R

// Tanpa receiver — blok eksekusi lokal
inline fun <R> run(block: () -> R): R
  • Receiver: objek dipanggil run (tersedia sebagai this).
  • Return: hasil block.
  • Use case utama: eksekusi block pada receiver, ambil hasil.

Tabel Perbandingan run dengan Scope Lain #

Aspekrun
Receiverthis (objek dipanggil run)
Returnlambda result
Idiomatic useEksekusi block pada receiver, ambil hasil
Bisa dipakai tanpa receiverYa — sebagai blok lokal

Pola run di Kode Nyata #

Mari kita lihat empat pola run yang sering muncul.

kotlin
// run_example.kt
// run: receiver `this`, mengembalikan lambda result.
// Cocok untuk eksekusi blok pada receiver dan mengembalikan hasil.

class StringBuilder2 {
    val parts = mutableListOf<String>()

    fun tambah(s: String) {
        parts.add(s)
    }

    fun build(): String = parts.joinToString(", ")
}

fun main() {
    // Pola 1: run pada instance untuk menggabungkan konfigurasi dan hasil.
    val pesan = StringBuilder2().run {
        tambah("Halo")
        tambah("Budi")
        tambah("2026")
        build()
    }
    println("Hasil run: $pesan")

    // Pola 2: run tanpa receiver — sebagai blok eksekusi lokal.
    val hasilHitung = run {
        val a = 10
        val b = 20
        val c = a + b
        "Total a + b + c = ${a + b + c}"
    }
    println(hasilHitung)

    // Pola 3: run pada nullable dengan safe-call.
    val nama: String? = "Siti"
    val panjang = nama?.run {
        println("Memproses nama: $this")
        length
    }
    println("Panjang: $panjang")

    // Pola 4: run vs with — run sebagai extension, with sebagai function biasa.
    val sb = StringBuilder()
    val html = sb.run {
        append("<html>")
        append("<body>Halo</body>")
        append("</html>")
        toString()
    }
    println("HTML: $html")
}

Output yang Dihasilkan:

text
Hasil run: Halo, Budi, 2026
Total a + b + c = 60
Memproses nama: Siti
Panjang: 4
HTML: <html><body>Halo</body></html>

Mari kita telaah baris-baris kode di atas:

  1. Baris 13-19 (val pesan = StringBuilder2().run { ... }): pola konfigurasi + hasil. Instance StringBuilder2 dibuat dan run dipanggil. Di dalam block, this adalah instance tersebut — kita memanggil tambah(...) tanpa prefix. Block ditutup dengan build() yang mengembalikan string. Hasil string menjadi nilai pesan.
  2. Baris 22-27 (val hasilHitung = run { ... }): pola run tanpa receiver. Bentuk ini adalah blok eksekusi lokal — beberapa variabel lokal dihitung dan ditutup dengan ekspresi string. Manfaat: variabel a, b, c tidak bocor ke scope luar.
  3. Baris 30-34 (val panjang = nama?.run { ... }): pola safe-call pada nullable. nama?.run hanya menjalankan block bila nama tidak null. Di dalam block, this adalah String non-null — kita memanggil length tanpa prefix. Bila nama null, hasil null.
  4. Baris 37-43 (val html = sb.run { append(...); ...; toString() }): pola extension + lambda result. sb adalah StringBuilder. Di dalam block, this adalah sb — kita memanggil append beberapa kali lalu toString() untuk menghasilkan string final. Pola ini sering dipakai untuk build string/HTML.

run vs with #

run dan with mirip — keduanya memakai this dan mengembalikan lambda result. Perbedaan: run adalah extension function (dipanggil pada objek), sedangkan with adalah function biasa (menerima objek sebagai parameter pertama).

Aspekrunwith
Bentukobj.run { ... }with(obj) { ... }
Receiverthisthis
Returnlambda resultlambda result
Nullable safetyBisa obj?.run { }Tidak bisa langsung

run Tanpa Receiver — Blok Lokal #

Salah satu penggunaan run yang kurang dikenal adalah sebagai blok eksekusi lokal tanpa receiver. Pola ini berguna untuk mengelompokkan beberapa perhitungan menjadi satu ekspresi tanpa membocorkan variabel lokal ke scope luar.

kotlin run sebagai blok lokal
val hasil = run {
    val a = bacaConfig()
    val b = hitungBerdasarkan(a)
    val c = validasi(b)
    c.toString()
}
// a, b, c tidak tersedia di luar block

Referensi Lanjutan #

Penutup #

run adalah scope function fleksibel yang dapat dipakai sebagai extension (menggunakan this) atau blok lokal (tanpa receiver). Dengan run, kita dapat mengelompokkan konfigurasi dan hasil dalam satu ekspresi, mengelola nullable dengan safe-call, dan membatasi scope variabel lokal. Selanjutnya, kita akan mempelajari with — function non-extension yang dipakai khusus untuk mengelompokkan panggilan pada objek yang sama.