Kotlin Academy · บทเรียน

การเขียนตัวแทน ReadWriteProperty แบบกำหนดเอง

สร้างตัวแทนพร็อพเพอร์ตีแบบกำหนดเองตั้งแต่ต้นด้วย ReadWriteProperty

บทเรียน 3 จาก 413 ขั้นตอน

การเขียนตัวแทน ReadWriteProperty แบบกำหนดเอง เป็นบทเรียน Kotlin Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Kotlin Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Kotlin Academy มีบทเรียนทั้งหมด 4 บทเรียน

อินเทอร์เฟซ ReadWriteProperty

ตัวแทนแบบกำหนดเองจะใช้งาน ReadWriteProperty<ThisRef, T> พร้อมตัวดำเนินการ getValue และ 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 สำหรับ val

สำหรับพร็อพเพอร์ตี val แบบอ่านอย่างเดียว ให้ใช้งาน ReadOnlyProperty<ThisRef, T> โดยมีเพียง 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) }

ใช้ thisRef สำหรับบริบท

thisRef ช่วยให้ตัวแทนเข้าถึงออบเจ็กต์ที่ครอบอยู่ได้ ทำให้สามารถทำงานโดยคำนึงถึงบริบท

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) }

ตัวแทนตัวเลขแบบจำกัดช่วง

ตัวแทนที่จำกัดค่าตัวเลขให้อยู่ในช่วงหนึ่ง และบังคับใช้เงื่อนไขคงที่อย่างโปร่งใส

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
}

ตัวแทนแคชค่าที่อาจเป็น null

ตัวแทนที่คำนวณค่าเพียงครั้งเดียวและแคชไว้ โดยสามารถทำให้แคชไม่ถูกต้องได้ด้วยการกำหนดค่าเป็น null

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
}

ตัวแทนที่ปลอดภัยต่อเธรดด้วย @Synchronized

เพิ่มความปลอดภัยระหว่างเธรดให้ตัวแทนด้วย @Synchronized หรือออบเจ็กต์ล็อกภายใน

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) }

รูปแบบฟังก์ชันโรงงานของตัวแทน

เปิดเผยตัวแทนผ่านฟังก์ชันส่วนขยายที่ส่งคืนออบเจ็กต์ตัวแทน โดยทำตามรูปแบบของไลบรารีมาตรฐาน

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" }

ตัวแทนพร้อมข้อมูลเมทาดาทาของ KProperty

อาร์กิวเมนต์ KProperty ให้ชื่อพร็อพเพอร์ตีและคำอธิบายประกอบ ซึ่งมีประโยชน์สำหรับเฟรมเวิร์กการบันทึกข้อมูลหรือ 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 */ }

ตัวดำเนินการ provideDelegate

provideDelegate ช่วยให้ตัวแทนดักจับการตั้งค่าการมอบหมาย ตรวจสอบชื่อพร็อพเพอร์ตีหรือคำอธิบายประกอบได้ในเวลาเริ่มต้น

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) }

เปรียบเทียบแนวทาง

ตัวแทนแบบกำหนดเองกับ observable/vetoable: ใช้ตัวแทนในตัวสำหรับรูปแบบการตอบสนองแบบง่าย และเขียนตัวแทนแบบกำหนดเองสำหรับเงื่อนไขคงที่ที่ซับซ้อน การแคช หรือการทำงานที่คำนึงถึงบริบท

// 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) }

ภาคปฏิบัติ: ดีลีเกตของ SharedPreferences

รูปแบบที่ใช้กันทั่วไปใน Android: มอบหมายพร็อพเพอร์ตีให้กับ SharedPreferences เพื่อให้การอ่านและการเขียนถูกบันทึกลงดิสก์โดยอัตโนมัติ

// 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())
}

ตรวจสอบความเข้าใจ

ดีลีตสำหรับอ่านและเขียนต้องใช้อินเทอร์เฟซใด

สรุปทบทวน

ดีลีตแบบกำหนดเองใช้งาน ReadWriteProperty (หรือ ReadOnlyProperty) และสามารถซ่อนการบันทึกข้อมูลการทำงาน การแคช การตรวจสอบความถูกต้อง การจำกัดค่า หรือการจัดเก็บบนแพลตฟอร์มไว้เบื้องหลังอินเทอร์เฟซพร็อพเพอร์ตีที่ใช้งานได้อย่างโปร่งใส

เริ่มต้นได้ฟรี

เรียนรู้ Kotlin ด้วย AI tutor — ฟรี

เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป

คอร์ส
51
บทเรียน
203

คำถามที่พบบ่อย

บทเรียน “การเขียนตัวแทน ReadWriteProperty แบบกำหนดเอง” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การเขียนตัวแทน ReadWriteProperty แบบกำหนดเอง” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Kotlin Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Kotlin Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การเขียนตัวแทน ReadWriteProperty แบบกำหนดเอง”

สร้างตัวแทนพร็อพเพอร์ตีแบบกำหนดเองตั้งแต่ต้นด้วย ReadWriteProperty คุณปฏิบัติ Kotlin Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Kotlin Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Kotlin Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “การเขียนตัวแทน ReadWriteProperty แบบกำหนดเอง” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Kotlin Academy นี้ได้ไหม

ได้ บทเรียน Kotlin Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. by lazy: เจาะลึกการเริ่มต้นแบบเลื่อนเวลา
  2. ตัวแทน observable และ vetoable
  3. การเขียนตัวแทน ReadWriteProperty แบบกำหนดเอง
  4. ตัวแทนสำหรับ Preferences พร็อพเพอร์ตีที่ใช้ Map และการบันทึก
← กลับไปที่ Kotlin Academy