Kotlin Academy · 课时

委托给成员

组合行为

第 2 / 4 课13 个步骤

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

组合行为

现实中的对象通常需要组合多种能力。您不必从多个来源继承,而是可以将每项能力委托给专门的成员对象。

两个角色,一个类

假设一个类既要读取又要写入。请定义两个接口,并分别将它们委托给各自的实现。

interface Reader { fun read(): String }
interface Writer { fun write(s: String): String }

class FileReader : Reader { override fun read() = "contents" }
class FileWriter : Writer { override fun write(s: String) = "wrote " + s }

class FileIO(r: Reader, w: Writer) : Reader by r, Writer by w

fun main() {
    val io = FileIO(FileReader(), FileWriter())
    println(io.read())
    println(io.write("data"))
}

多个委托对象

一个类可以同时委托多个接口,并将每个接口委托给不同的成员。这样,类无需继承就能组合这些成员的行为。

interface Logger { fun log(m: String): String }
interface Validator { fun valid(m: String): Boolean }

class ConsoleLogger : Logger { override fun log(m: String) = "LOG: " + m }
class LengthValidator : Validator { override fun valid(m: String) = m.isNotEmpty() }

class Service(l: Logger, v: Validator) : Logger by l, Validator by v

fun main() {
    val s = Service(ConsoleLogger(), LengthValidator())
    println(s.valid("hi"))
    println(s.log("started"))
}

将委托对象存储为成员

当您需要在重写成员中引用委托对象时,请将它保存为构造函数属性。同一个实例会同时支持转发逻辑和您的自定义代码。

interface Cache { fun get(k: String): String }

class MapCache : Cache {
    override fun get(k: String) = "value-of-" + k
}

class TracingCache(private val inner: Cache) : Cache by inner {
    override fun get(k: String): String {
        println("lookup " + k)
        return inner.get(k)
    }
}

fun main() {
    println(TracingCache(MapCache()).get("id"))
}

组合委托与自有状态

委托类可以在转发成员之外添加自己的属性和方法,从而丰富组合得到的对象。

interface Engine { fun start(): String }
class V8 : Engine { override fun start() = "vroom" }

class Car(e: Engine) : Engine by e {
    var doors = 4
    fun honk() = "beep"
}

fun main() {
    val c = Car(V8())
    println(c.start())
    println(c.honk())
    println(c.doors)
}

替换实现

由于委托对象只是一个成员,您可以使用不同的底层实现来构造同一个包装器,从而在运行时选择行为。

interface Notifier { fun send(msg: String): String }
class EmailNotifier : Notifier { override fun send(msg: String) = "email: " + msg }
class SmsNotifier : Notifier { override fun send(msg: String) = "sms: " + msg }

class Alerts(n: Notifier) : Notifier by n

fun main() {
    println(Alerts(EmailNotifier()).send("hi"))
    println(Alerts(SmsNotifier()).send("hi"))
}

委托给属性

by 后面的表达式可以是任何只求值一次的表达式,包括构造函数调用。它不一定必须是参数。

interface Greeter { fun greet() = "hi" }
class DefaultGreeter : Greeter

class Page : Greeter by DefaultGreeter()

fun main() {
    println(Page().greet())
}

避免菱形问题

类无法实现行为的多重继承,但将多个接口委托给不同成员,可以干净地避开经典的菱形问题。

interface A { fun a() = "A" }
interface B { fun b() = "B" }
class AImpl : A
class BImpl : B

class Combined : A by AImpl(), B by BImpl()

fun main() {
    val c = Combined()
    println(c.a() + c.b())
}

面向组合进行设计

要实现良好的组合:

  • 定义小而专注的接口
  • 提供可复用的实现
  • 在组合类中委托每个角色

这样可以让类保持灵活且易于测试。

成员委托回顾

委托给成员对象可以让类从各个部分组装行为。您可以组合多个接口、有选择地重写成员、替换实现并添加自己的状态——整个过程无需继承。

下一步:直接比较

您已经了解了委托的灵活性。下一课将直接比较委托与继承,帮助您知道何时选择哪一种方式。

快速检查

请检验您对成员委托的理解。

回顾

您学到了成员委托:

  • 将多个接口委托给不同对象
  • 有选择地重写成员并添加自有状态
  • 在构造时替换实现
  • 避免多重继承带来的问题

下一步:委托与继承。

免费开始

用 AI 导师学习 Kotlin — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
51
课程
203

常见问题解答

「委托给成员」课时是免费的吗?

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

「委托给成员」这节课中我会学到什么?

组合行为 你通过在浏览器中直接运行的动手代码来练习 Kotlin Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Kotlin Academy 需要有经验吗?

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

「委托给成员」课时需要多长时间?

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

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

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

此课程中的所有课时

  1. by 关键字
  2. 委托给成员
  3. 委托与继承
  4. 实用模式
← 返回 Kotlin Academy