0Pricing
Kotlin Academy · Lekcja

Pisanie własnego delegata ReadWriteProperty

Zaimplementuj własny delegat właściwości od podstaw, korzystając z ReadWriteProperty.

Pisanie własnego delegata ReadWriteProperty to bezpłatna lekcja Kotlin Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Kotlin Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Kotlin Academy zawiera 4 lekcji w sumie.

Interfejs ReadWriteProperty

Własny delegat implementuje ReadWriteProperty<ThisRef, T> za pomocą operatorów getValue i setValue.

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class SimpleDelegate<T>(private var value: T) : ReadWriteProperty<Any?, T> {
    override fun getValue(thisRef: Any?, property: KProperty<*>): T = value
    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        this.value = value
    }
}
var x: Int by SimpleDelegate(0)
fun main() { x = 42; println(x) }

ReadOnlyProperty dla val

W przypadku właściwości tylko do odczytu val należy zaimplementować ReadOnlyProperty<ThisRef, T>, używając wyłącznie getValue.

import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
class Constant<T>(private val value: T) : ReadOnlyProperty<Any?, T> {
    override fun getValue(thisRef: Any?, property: KProperty<*>): T = value
}
val pi: Double by Constant(3.14159)
fun main() { println(pi) }

Używanie thisRef do zapewnienia kontekstu

thisRef zapewnia delegatowi dostęp do obiektu zewnętrznego, umożliwiając działanie zależne od kontekstu.

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class LoggingDelegate<T>(private var v: T) : ReadWriteProperty<Any?, T> {
    override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        println("[${thisRef?.javaClass?.simpleName}] get ${property.name} = $v")
        return v
    }
    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        println("[${thisRef?.javaClass?.simpleName}] set ${property.name} = $value")
        v = value
    }
}
class Config { var host: String by LoggingDelegate("localhost") }
fun main() { val c = Config(); c.host = "prod.example.com"; println(c.host) }

Delegat ograniczający wartość liczbową

Delegat ograniczający wartość liczbową do określonego zakresu i w przejrzysty sposób wymuszający niezmienniki.

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class Clamped(private val min: Int, private val max: Int, initial: Int) : ReadWriteProperty<Any?, Int> {
    private var v = initial.coerceIn(min, max)
    override fun getValue(t: Any?, p: KProperty<*>) = v
    override fun setValue(t: Any?, p: KProperty<*>, value: Int) {
        v = value.coerceIn(min, max)
    }
}
var brightness: Int by Clamped(0, 255, 128)
fun main() {
    brightness = 300; println(brightness) // 255
    brightness = -5;  println(brightness) // 0
    brightness = 100; println(brightness) // 100
}

Delegat buforujący wartości nullable

Delegat, który oblicza wartość raz i ją buforuje, a po ustawieniu wartości null pozwala unieważnić bufor.

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class CachedNullable<T>(private val compute: () -> T) : ReadWriteProperty<Any?, T?> {
    private var cache: T? = null
    override fun getValue(t: Any?, p: KProperty<*>): T? = cache ?: compute().also { cache = it }
    override fun setValue(t: Any?, p: KProperty<*>, value: T?) { cache = value }
}
var expensiveResult: Int? by CachedNullable { (1..1000).sum() }
fun main() {
    println(expensiveResult) // computed
    println(expensiveResult) // cached
    expensiveResult = null
    println(expensiveResult) // recomputed
}

Delegat bezpieczny wątkowo z @Synchronized

Bezpieczeństwo wątkowe delegata można zapewnić za pomocą @Synchronized lub wewnętrznego obiektu blokady.

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class SynchronizedDelegate<T>(private var v: T) : ReadWriteProperty<Any?, T> {
    @Synchronized
    override fun getValue(t: Any?, p: KProperty<*>) = v
    @Synchronized
    override fun setValue(t: Any?, p: KProperty<*>, value: T) { v = value }
}
var counter: Int by SynchronizedDelegate(0)
fun main() { counter = 99; println(counter) }

Wzorzec fabrycznej funkcji delegata

Delegat można udostępnić za pomocą funkcji rozszerzającej zwracającej obiekt delegata, zgodnie ze stylem biblioteki standardowej.

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
fun <T> logged(initial: T): ReadWriteProperty<Any?, T> = object : ReadWriteProperty<Any?, T> {
    var v = initial
    override fun getValue(t: Any?, p: KProperty<*>): T { println("get ${p.name}"); return v }
    override fun setValue(t: Any?, p: KProperty<*>, value: T) { println("set ${p.name}=$value"); v = value }
}
var name: String by logged("world")
fun main() { println(name); name = "Kotlin" }

Delegat z metadanymi KProperty

Argument KProperty udostępnia nazwę właściwości i adnotacje, co jest przydatne w strukturach do rejestrowania lub w systemach ORM.

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class AuditDelegate<T>(private var v: T) : ReadWriteProperty<Any?, T> {
    private val history = mutableListOf<String>()
    override fun getValue(t: Any?, p: KProperty<*>) = v
    override fun setValue(t: Any?, p: KProperty<*>, value: T) {
        history.add("${p.name}: $v -> $value")
        v = value
    }
    fun printHistory() = history.forEach(::println)
}
var status: String by AuditDelegate("idle").also { /* expose audit */ }

Operator provideDelegate

provideDelegate pozwala delegatowi przechwycić konfigurację delegowania oraz zweryfikować nazwę właściwości lub adnotacje podczas inicjalizacji.

import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
class ValidatedDelegate<T>(private val v: T) {
    operator fun provideDelegate(t: Any?, p: KProperty<*>): ReadOnlyProperty<Any?, T> {
        require(p.name.isNotBlank()) { "Property name must not be blank" }
        return ReadOnlyProperty { _, _ -> v }
    }
}
val greeting: String by ValidatedDelegate("Hello")
fun main() { println(greeting) }

Porównanie podejść

Własne delegaty a observable/vetoable: w przypadku prostych wzorców reaktywnych należy używać wbudowanych rozwiązań, a własne delegaty pisać dla złożonych niezmienników, buforowania lub działania zależnego od kontekstu.

// Built-in: simple change notification
import kotlin.properties.Delegates
var simple: Int by Delegates.observable(0) { _,_,v -> println("changed: $v") }
// Custom: clamping + logging
var complex: Int by run {
    object : kotlin.properties.ReadWriteProperty<Any?,Int> {
        var v = 0
        override fun getValue(t:Any?,p:kotlin.reflect.KProperty<*>)=v
        override fun setValue(t:Any?,p:kotlin.reflect.KProperty<*>,value:Int){
            println("clamping $value"); v=value.coerceIn(0,100)
        }
    }
}
fun main() { simple=5; complex=200; println(complex) }

Praktyka: delegat SharedPreferences

Popularny wzorzec w Androidzie: delegowanie właściwości do SharedPreferences sprawia, że odczyty i zapisy są automatycznie utrwalane na dysku.

// Pseudocode (no Android runtime here)
class PrefDelegate(private val key: String, private val default: String) {
    // In real Android code:
    // override fun getValue(...) = prefs.getString(key, default) ?: default
    // override fun setValue(...) { prefs.edit().putString(key, value).apply() }
    fun getValue(): String = default // simplified
}
fun main() {
    val delegate = PrefDelegate("user_name", "Guest")
    println(delegate.getValue())
}

Szybkie sprawdzenie

Jaki interfejs musi implementować delegat odczytu i zapisu?

Podsumowanie

Własne delegaty implementują ReadWriteProperty (lub ReadOnlyProperty) i mogą ukrywać logowanie, buforowanie, walidację, ograniczanie wartości lub magazynowanie na platformie za przejrzystym interfejsem właściwości.

Często zadawane pytania

Czy lekcja „Pisanie własnego delegata ReadWriteProperty” jest bezpłatna?

Tak — pełny tekst „Pisanie własnego delegata ReadWriteProperty” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Kotlin Academy, przejdź na CoddyKit PRO. Kurs Kotlin Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Pisanie własnego delegata ReadWriteProperty”?

Zaimplementuj własny delegat właściwości od podstaw, korzystając z ReadWriteProperty. Ćwiczysz Kotlin Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Kotlin Academy?

Nie wymagamy żadnego doświadczenia. Kotlin Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.

Ile czasu zajmuje lekcja „Pisanie własnego delegata ReadWriteProperty”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Kotlin Academy?

Tak. Każda lekcja Kotlin Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. by lazy: szczegółowe omówienie odroczonej inicjalizacji
  2. Delegaty observable i vetoable
  3. Pisanie własnego delegata ReadWriteProperty
  4. Delegaty dla Preferences, właściwości opartych na Map i logowania
← Powrót do Kotlin Academy