Menu
CoddyTech

Delegasi dengan by

Termasuk bagian Pemrograman Berorientasi Objek dalam perjalanan Kotlin di Coddy. Pelajaran 46 dari 57.

Pendelegasian berarti sebuah object menyerahkan pekerjaan kepada object lain, alih-alih mewarisinya. Kotlin mendukungnya secara langsung: class Polite(private val inner: Greeter) : Greeter by inner mengimplementasikan Greeter dengan meneruskan setiap pemanggilan ke inner, dan class hanya menimpa bagian yang diubahnya:

interface Greeter {
    fun hello(name: String): String
    fun bye(name: String): String
}
class English : Greeter {
    override fun hello(name: String) = "Hello, $name"
    override fun bye(name: String) = "Bye, $name"
}
class Polite(private val inner: Greeter) : Greeter by inner {
    override fun hello(name: String) = inner.hello(name) + ", pleased to meet you"
}

Di dalam main:

val g = Polite(English())
println(g.hello("Ada"))
println(g.bye("Ada"))

Output:

Hello, Ada, pleased to meet you
Bye, Ada

Delegasi adalah alternatif untuk pewarisan. Delegasi bekerja dengan interface yang implementasinya bersifat final, dan delegate bisa berupa object apa pun yang dipilih saat program berjalan. Set ini menghitung setiap add dan meneruskan hal lainnya ke MutableSet biasa:

class CountingSet<T>(private val inner: MutableSet<T> = mutableSetOf()) : MutableSet<T> by inner {
    var attempts = 0
        private set

    override fun add(element: T): Boolean {
        attempts++
        return inner.add(element)
    }
}

Di dalam main:

val tags = CountingSet<String>()
tags.add("kotlin")
tags.add("jvm")
tags.add("kotlin")
println("${tags.size} tags after ${tags.attempts} adds, contains jvm: ${"jvm" in tags}")

Output:

2 tags after 3 adds, contains jvm: true

Properti yang didelegasikan menyerahkan getter dan setter suatu properti kepada object lain, sekali lagi dengan by. lazy dari bab 2 adalah salah satu delegate, dan map juga bisa menjadi delegate: setiap properti membaca entri dengan namanya sendiri:

class User(values: Map<String, Any?>) {
    val name: String by values
    val age: Int by values
}

Di dalam main:

val u = User(mapOf("name" to "Ada", "age" to 36))
println("${u.name} is ${u.age}")

Output:

Ada is 36

Kamu bisa menulis delegate property sendiri: sebuah class dengan operator fun getValue, serta setValue untuk var. Kotlin memanggilnya setiap kali property dibaca atau ditulis. Delegate ini memangkas setiap teks yang ditetapkan dan menghitung perubahan:

import kotlin.reflect.KProperty

class Trimmed {
    private var value = ""
    var changes = 0

    operator fun getValue(thisRef: Any?, property: KProperty<*>) = value
    operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: String) {
        value = newValue.trim()
        changes++
    }
}

class Form {
    val nameField = Trimmed()
    var name: String by nameField
}

Di dalam main:

val form = Form()
form.name = "  Ada  "
form.name = "Ada L. "
println("[${form.name}] after ${form.nameField.changes} changes")

Output:

[Ada L.] after 2 changes
challenge icon

Tantangan

Mudah

Antarmuka Storage (dengan put, get, dan keys) serta implementasinya, MapStorage, sudah disediakan. Tulis dua dekorator dengan delegasi class, masing-masing hanya meng-override put: LoggingStorage(inner) mencatat put a=1 dalam list log yang hanya bisa dibaca, lalu meneruskan pemanggilannya, sedangkan LimitedStorage(inner, max) menolak (mengembalikan false) penambahan key baru setelah ada max key, tetapi pembaruan key yang sudah ada tetap diizinkan. Semua hal lainnya diteruskan dengan by.

Kode yang disediakan membungkus MapStorage dengan LoggingStorage dan, jika baris pertama berisi batas positif, membungkusnya dengan LimitedStorage. Kode tersebut menjalankan perintah put a 1, get a, dan keys, lalu mencetak ok, refused, nilainya atau missing, dan daftar key, lalu mencetak log: put a=1, ....

Tulis kodemu di LoggingStorage.kt, LimitedStorage.kt, Storage.kt, dan MapStorage.kt. Main.kt berisi kode input/output yang disediakan dan tidak boleh diedit.

Coba sendiri

fun main() {
    // Kode input/output yang disediakan: biarkan tetap seperti ini
    val input = generateSequence(::readLine).toList()
    val base = MapStorage()
    val logging = LoggingStorage(base)
    val limit = input[0].toInt()
    val storage: Storage = if (limit > 0) LimitedStorage(logging, limit) else logging
    for (cmd in input.drop(1)) {
        val p = cmd.split(" ")
        when (p[0]) {
            "put" -> println(if (storage.put(p[1], p[2])) "ok" else "refused")
            "get" -> println(storage.get(p[1]) ?: "missing")
            else -> println(storage.keys())
        }
    }
    println("log: " + logging.log.joinToString(", ").ifEmpty { "empty" })
}
quiz iconUji dirimu

Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.

Semua pelajaran di Pemrograman Berorientasi Objek

Latihan sendiri: Playground Kotlin