with adalah satu-satunya scope function yang bukan extension function. Function ini menerima objek sebagai parameter pertama dan menjalankan block dengan this merujuk ke objek tersebut. Pada chapter ini kita akan melihat kapan with lebih cocok dipakai daripada run, bagaimana with membantu mengelompokkan beberapa panggilan pada objek yang sama, dan kapan with lebih natural daripada extension scope function. Kita juga akan melihat perbandingan langsung dengan run.

Apa Itu with #

Signature with:

kotlin Signature with
inline fun <T, R> with(receiver: T, block: T.() -> R): R
  • Bentuk: function biasa, BUKAN extension.
  • Receiver: parameter pertama receiver (tersedia sebagai this di block).
  • Return: hasil block.
  • Use case utama: mengelompokkan beberapa panggilan pada objek yang sama.

Tabel Perbandingan with dengan Scope Lain #

Aspekwith
Bentukfunction biasa with(obj) { ... }
Receiverthis (parameter pertama)
Returnlambda result
Idiomatic useMengelompokkan panggilan pada objek yang sama
Bisa safe-callTidak — with tidak extension

Pola with di Kode Nyata #

Mari kita lihat empat pola with yang umum.

kotlin
// with_example.kt
// with: BUKAN extension function.
// Menerima objek, mengembalikan lambda result. Receiver `this`.

data class Konfigurasi(
    var host: String = "",
    var port: Int = 0,
    var timeoutMs: Int = 0,
    var debug: Boolean = false
)

fun main() {
    val config = Konfigurasi()

    // Pola 1: dengan `with` kita mengelompokkan beberapa panggilan pada objek.
    val ringkasan = with(config) {
        host = "127.0.0.1"
        port = 8080
        timeoutMs = 5_000
        debug = true
        "Konfigurasi: $host:$port (timeout=${timeoutMs}ms, debug=$debug)"
    }
    println(ringkasan)

    // Pola 2: `with` untuk menghitung sesuatu dari beberapa property.
    val tugas = mutableListOf("Belajar", "Latihan", "Proyek")
    val jumlahTugas = with(tugas) {
        val total = size
        val selesai = count { it.length > 7 }
        "Total $total, selesai $selesai"
    }
    println(jumlahTugas)

    // Pola 3: perbandingan dengan run.
    val sb = StringBuilder()
    val hasilWith = with(sb) {
        append("Halo, ")
        append("Budi!")
        toString()
    }
    println("Dengan with: $hasilWith")

    // Pola 4: return value bukan objek.
    val angka = 42
    val deskripsi = with(angka) {
        when {
            this % 2 == 0 -> "genap"
            else -> "ganjil"
        }
    }
    println("42 adalah $deskripsi")
}

Output yang Dihasilkan:

text
Konfigurasi: 127.0.0.1:8080 (timeout=5000ms, debug=true)
Total 3, selesai 0
Dengan with: Halo, Budi!
42 adalah genap

Mari kita telaah baris-baris kode di atas:

  1. Baris 8-13 (data class Konfigurasi(...)): data class dengan beberapa mutable property. Kondisi ideal untuk with karena kita akan mengubah beberapa property sekaligus.
  2. Baris 17-24 (val ringkasan = with(config) { ... }): pola mengelompokkan konfigurasi. with(config) mengikat this ke config. Di dalam block, kita mengubah empat property (host, port, timeoutMs, debug) tanpa prefix. Block ditutup dengan string ringkasan yang menjadi nilai ringkasan. Tanpa with, kita menulis config.host = ...; config.port = ... — prefix berulang.
  3. Baris 27-32 (with(tugas) { ... }): pola menghitung dari beberapa property. Di dalam block, size dan count { ... } dipanggil pada tugas. Block mengembalikan string deskripsi. Variabel total dan selesai tidak bocor ke scope luar.
  4. Baris 35-40 (with(sb) { append(...); append(...); toString() }): pola build string. Di dalam block, append dipanggil tanpa prefix. Block ditutup dengan toString() yang menghasilkan string. Pola ini sering muncul saat membuat dokumen HTML/XML/JSON.
  5. Baris 43-49 (with(angka) { when { ... } }): pola return non-objek. Block tidak harus mengembalikan objek receiver. Di sini block mengembalikan string "genap" atau "ganjil" berdasarkan kondisi this % 2 == 0.

Kapan with Lebih Cocok Daripada run #

with lebih cocok dipakai saat:

  • Objek sudah non-null dan pasti tidak null.
  • Kita ingin mengelompokkan beberapa panggilan pada objek yang sama.
  • Sintaks function biasa terasa lebih alami (memanggil with(obj) { ... } di awal statement).
kotlin Perbandingan with vs run
// with — function biasa, terbaca natural untuk objek non-null
val ringkasan = with(config) {
    host = "127.0.0.1"
    port = 8080
    "config: $host:$port"
}

// run — extension, cocok untuk nullable
val ringkasan2 = config?.run {
    host = "127.0.0.1"
    port = 8080
    "config: $host:$port"
}

with untuk Reduksi Pengulangan #

Saat kita memanggil banyak method pada objek yang sama dalam satu blok kode, with dapat mengurangi pengulangan prefix:

kotlin with untuk reduksi prefix
val sb = StringBuilder()
with(sb) {
    append("Baris 1\n")
    append("Baris 2\n")
    append("Baris 3\n")
}
val hasil = sb.toString()

Tanpa with, kita menulis sb.append(...) tiga kali. Dengan with, prefix sb. dihilangkan dari setiap panggilan.

Referensi Lanjutan #

Penutup #

with adalah scope function untuk mengelompokkan beberapa panggilan pada objek yang sama. Karena bukan extension function, with tidak dapat dipakai dengan safe-call — bila objek bisa null, gunakan run. Dengan with, kode yang mengubah banyak property pada satu objek menjadi lebih ringkas dan terfokus. Selanjutnya, kita akan mempelajari apply — scope function yang mengembalikan objek itu sendiri, sangat populer untuk konfigurasi builder.