0Pricing
Kotlin Academy · 课时

observable 与 vetoable 委托

使用 observable 响应属性变化,并使用 vetoable 按条件拒绝变化。

observable 与 vetoable 委托 是 CoddyKit 上的免费 Kotlin Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Kotlin Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Kotlin Academy 课程共包含 4 节课。

属性委托复习

Kotlin 属性委托允许独立对象处理 getValue 和 setValue。Delegates.observable 和 Delegates.vetoable 是标准库提供的内置委托。

import kotlin.properties.Delegates
var name: String by Delegates.observable("initial") { prop, old, new ->
    println("${prop.name}: $old -> $new")
}
fun main() { name = "Alice"; name = "Bob" }

observable 语法

observable 接收一个初始值和一个回调。每次更改后都会调用该回调,并传入属性、旧值和新值。

import kotlin.properties.Delegates
var score: Int by Delegates.observable(0) { _, old, new ->
    println("Score changed from $old to $new")
}
fun main() {
    score = 10
    score = 25
    score = 0
}

触发界面更新

当支持属性发生变化时,Observable 委托非常适合触发界面刷新,而不需要完整配置 LiveData。

import kotlin.properties.Delegates
class ViewModel {
    var title: String by Delegates.observable("Loading") { _, _, new ->
        println("Render: $new") // pretend this updates UI
    }
}
fun main() {
    val vm = ViewModel()
    vm.title = "Home"
    vm.title = "Settings"
}

vetoable 语法

vetoable 会在赋值之前调用回调。如果回调返回 false,新值就会被拒绝,并保留旧值。

import kotlin.properties.Delegates
var age: Int by Delegates.vetoable(0) { _, old, new ->
    new >= 0 // reject negative ages
}
fun main() {
    age = 25; println(age)  // 25
    age = -1; println(age)  // 25 (rejected)
    age = 30; println(age)  // 30
}

使用 vetoable 组合验证

使用 vetoable 在属性级别强制执行领域不变量,而不需要外部验证逻辑。

import kotlin.properties.Delegates
var email: String by Delegates.vetoable("") { _, _, new ->
    new.contains("@")
}
fun main() {
    email = "user@example.com"; println(email) // set
    email = "invalid";          println(email) // still previous
}

使用 observable 记录更改日志

使用 observable 进行审计日志记录:自动记录每次状态转换,而无需修改业务逻辑。

import kotlin.properties.Delegates
val log = mutableListOf<String>()
var status: String by Delegates.observable("IDLE") { _, old, new ->
    log.add("$old -> $new")
}
fun main() {
    status = "RUNNING"
    status = "DONE"
    println(log)
}

类中的 observable

这两个委托都可以像其他属性一样在类主体中使用。

import kotlin.properties.Delegates
class Counter {
    var count: Int by Delegates.observable(0) { _, old, new ->
        if (new > old) println("Incremented to $new")
        else println("Decremented to $new")
    }
}
fun main() {
    val c = Counter()
    c.count = 5
    c.count = 3
}

使用 vetoable 约束范围

使用 vetoable 将数值属性限制在有效范围内。

import kotlin.properties.Delegates
var volume: Int by Delegates.vetoable(50) { _, _, new ->
    new in 0..100
}
fun main() {
    volume = 80;  println(volume) // 80
    volume = 150; println(volume) // 80 (rejected)
    volume = 0;   println(volume) // 0
}

串联委托

您可以手动组合 observable 逻辑:编写一个自定义委托,在内部调用 observable,并在每个步骤中添加额外逻辑。

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class LoggedDelegate<T>(initial: T) : ReadWriteProperty<Any?, T> {
    private var value: T = initial
    override fun getValue(t: Any?, p: KProperty<*>) = value
    override fun setValue(t: Any?, p: KProperty<*>, v: T) {
        println("Setting ${p.name} = $v"); value = v
    }
}
var x: Int by LoggedDelegate(0)
fun main() { x = 10; x = 20 }

线程安全说明

observable 和 vetoable 默认都不是线程安全的。对于并发访问,请使用互斥锁保护修改操作,或在外部进行同步。

import kotlin.properties.Delegates
// Not thread-safe as-is:
var sharedState: String by Delegates.observable("start") { _, old, new ->
    println("$old -> $new")
}
// In coroutines, wrap access with Mutex if needed

实践模式:表单验证

为每个字段使用 vetoable,实现声明式表单验证:每个字段在赋值时自行验证。

import kotlin.properties.Delegates
data class Form(
    var username: String = "",
    var password: String = ""
) {
    var validUsername: String by Delegates.vetoable("") { _,_,v -> v.length >= 3 }
    var validPassword: String by Delegates.vetoable("") { _,_,v -> v.length >= 8 }
}
fun main() {
    val f = Form()
    f.validUsername = "ab";       println(f.validUsername) // ""
    f.validUsername = "alice";    println(f.validUsername) // "alice"
    f.validPassword = "short";    println(f.validPassword) // ""
    f.validPassword = "secure123";println(f.validPassword) // "secure123"
}

快速检查

vetoable 何时调用其回调?

回顾

observable 会在每次更改后发出通知;vetoable 会在赋值前运行,并可以拒绝新值。两者都非常适合在不依赖外部库的情况下实现日志记录、验证和响应式属性模式。

常见问题解答

「observable 与 vetoable 委托」课时是免费的吗?

是的 — 「observable 与 vetoable 委托」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Kotlin Academy 课程的其余内容,请升级到 CoddyKit PRO。 Kotlin Academy 课程共包含 4 节课。

「observable 与 vetoable 委托」这节课中我会学到什么?

使用 observable 响应属性变化,并使用 vetoable 按条件拒绝变化。 你通过在浏览器中直接运行的动手代码来练习 Kotlin Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Kotlin Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Kotlin Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「observable 与 vetoable 委托」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Kotlin Academy 课中编写并运行代码吗?

能。每节 Kotlin Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. by lazy:深入了解延迟初始化
  2. observable 与 vetoable 委托
  3. 编写自定义 ReadWriteProperty 委托
  4. 用于偏好设置、Map 支持属性和日志记录的委托
← 返回 Kotlin Academy