Property di Kotlin biasanya menyimpan nilai sendiri pada field di dalam class. Namun untuk kasus seperti inisialisasi tertunda, pencatatan audit, atau validasi nilai, pola get/set manual menjadi repetitif dan rentan kesalahan. Kotlin menyediakan property delegation yang memindahkan logika akses property ke objek lain lewat keyword by. Pada chapter ini kita akan membongkar konsep dasar delegation, melihat bagaimana compiler menerjemahkan by menjadi pemanggilan getValue/setValue, dan mempraktikkannya lewat dua delegate buatan sendiri. Pemahaman ini menjadi landasan bagi chapter-chapter berikutnya yang membahas lazy, observable, vetoable, hingga custom delegate.

Apa Itu Property Delegation #

Sebuah property didelegasikan ketika logika bacanya (get) dan tulisnya (set) dialihkan ke objek lain yang disebut delegate. Sintaksis dasarnya seperti berikut:

kotlin Sintaksis umum delegated property
val x: Int by delegate

var y: String by delegateLain

Kompilator menerjemahkan val x by delegate menjadi pemanggilan delegate.getValue(thisRef, property). Untuk var y by delegateLain, penugasan y = "foo" diterjemahkan menjadi delegateLain.setValue(thisRef, property, "foo"). Karena itu, objek delegate tidak perlu tahu apa pun tentang property — cukup menyediakan dua operator function tersebut.

Kontrak formalnya didefinisikan oleh dua interface pada package kotlin.properties:

InterfaceWajib padaCocok untuk
ReadOnlyProperty<R, T>getValue(thisRef, property): Tval
ReadWriteProperty<R, T>getValue + setValue(...)var

R adalah tipe receiver (biasanya class tempat property berada, atau Any? bila delegate generik), sedangkan T adalah tipe nilai yang dikeluarkan.

Delegate Sederhana Buku Sendiri #

Mari kita bangun dua delegate kecil untuk merasakan mekanismenya. File konsep_property_delegation.kt berisi class Konstan (read-only, mengembalikan nilai tetap) dan PencatatVar (read-write, mencatat jejak setiap perubahan):

kotlin
// File: konsep_property_delegation.kt
// Mendemonstrasikan konsep property delegation: keyword `by` menyerahkan
// logika get/set ke objek delegate lain.
package com.example.delegasi

import kotlin.properties.ReadOnlyProperty
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.jvm.isAccessible

// Delegate sederhana: menyimpan nilai konstan.
class Konstan<T>(private val nilai: T) : ReadOnlyProperty<Any?, T> {
    override fun getValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>): T = nilai
}

// Delegate read-write yang mencatat setiap perubahan.
class PencatatVar<T>(initial: T) : ReadWriteProperty<Any?, T> {
    private var nilai: T = initial
    private val riwayat = mutableListOf<T>()

    override fun getValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>): T = nilai

    override fun setValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>, value: T) {
        riwayat.add(nilai)
        nilai = value
    }

    fun jejak(): List<T> = riwayat.toList()
}

class Aplikasi {
    val versi: String by Konstan("1.0.0")
    val tahun: Int by Konstan(2026)

    var status: String by PencatatVar("idle")
}

fun main() {
    val app = Aplikasi()
    println("Versi: ${app.versi}")
    println("Tahun: ${app.tahun}")

    println("Status awal: ${app.status}")
    app.status = "loading"
    app.status = "ready"
    println("Status akhir: ${app.status}")

    // Akses delegate via KProperty.getDelegate(instance).
    val statusProp = Aplikasi::status
    statusProp.isAccessible = true
    val delegasi = statusProp.getDelegate(app)
    if (delegasi is PencatatVar<*>) {
        println("Jejak perubahan status: ${delegasi.jejak()}")
    }

    // Demonstrasi: versi dan tahun memakai delegate Konstan yang sama jenisnya.
    val versiProp = Aplikasi::versi
    versiProp.isAccessible = true
    val versiDelegate = versiProp.getDelegate(app)
    println("Delegate versi (class): ${versiDelegate?.javaClass?.simpleName}")
}

Output yang Dihasilkan:

text
Versi: 1.0.0
Tahun: 2026
Status awal: idle
Status akhir: ready
Jejak perubahan status: [idle, loading]
Delegate versi (class): Konstan

Mari kita bedah baris-baris penting pada kode di atas:

  1. Baris 11-13 (class Konstan<T> : ReadOnlyProperty<Any?, T>): delegate val. Konstruktor menerima nilai dan getValue selalu mengembalikan nilai yang sama. Setiap property val yang memakai delegate ini bersifat konstan — namun tetap berbeda dengan const val karena nilainya baru dipegang saat instance dibuat, bukan dikompilasi sebagai konstanta literal.
  2. Baris 16-28 (class PencatatVar<T> : ReadWriteProperty<Any?, T>): delegate var. Setiap kali setValue dipanggil, nilai lama ditambahkan ke riwayat sebelum diganti. Method jejak() mengembalikan salinan list agar mutasi dari luar tidak mengganggu state internal.
  3. Baris 30-35 (class Aplikasi): tiga property yang memakai kedua delegate. versi dan tahun memakai Konstan, sedangkan status memakai PencatatVar. Inilah inti delegation — logika berada di satu tempat dan dipakai ulang oleh banyak property.
  4. Baris 47-53 (akses delegate via reflection): KProperty.getDelegate(instance) mengembalikan objek delegate di balik sebuah property. Set isAccessible = true diperlukan bila property tidak public. Setelah itu kita dapat memanggil method delegate seperti jejak() secara langsung.
  5. Baris 56-59: demonstrasi serupa pada versi. Output mencetak nama class delegate, membuktikan bahwa versi dan tahun memakai instance Konstan yang berbeda walaupun jenisnya sama.

Mengapa Memakai Delegation #

Tanpa delegation, kita akan menulis backing field plus getter/setter custom pada setiap property yang butuh perilaku khusus. Dengan delegation, perilaku tersebut dirangkum dalam satu class delegate yang dapat dipakai pada banyak property, bahkan lintas class. Standard library Kotlin memanfaatkan pola ini untuk lazy, Delegates.observable, Delegates.vetoable, dan map-backed property — keempatnya akan kita bahas pada chapter-chapter berikutnya.

Referensi Lanjutan #

Selanjutnya #

Setelah memahami kontrak delegation, langkah berikutnya adalah memakai delegate paling umum di standard library — lazy — untuk inisialisasi tertunda yang hemat sumber daya. Materi tersebut akan kita bahas pada chapter lazy Delegation.