Kotlin委托

委托(Delegation)

大多数编程工作都涉及重用已有代码,有时只做少量改动。在面向对象编程中(Kotlin 也是如此),代码复用的主要手段是继承(以及组合),我们之前已经讲过了。这节内容我们讲另一种替代继承的方式——委托


委托的语法

委托是使用某个对象来完成接口的实现,而不是在当前类里重新写一遍实现。我们来看看具体怎么做。

假设我们有这样一段简单的代码——一个接口和它的实现:

interface MyInterface {
    fun print()
    val msg: String
}

class MyImplementation : MyInterface {
    override fun print() {
        println(msg)
    }

    override val msg: String = "MyImplementation sends regards!"
}

解释:
接口声明了一个属性和一个方法,类 MyImplementation 实现了它们。


现在,假设我们想创建一个新类,这个类要:

  1. 拥有自己的功能,

  2. 同时实现上述接口。

如果直接用继承,可能需要复制粘贴已有代码;但用委托就能避免这种重复。


用委托写法:

class MyNewClass(base: MyInterface) : MyInterface by base {
    override val msg = "Delegate sends regards."
}

解释:

  • 构造函数参数 base 期望传入一个 MyInterface 的实现。

  • 冒号后面写 MyInterface by base,表示 MyNewClass 实现了接口 MyInterface,但接口的具体实现“委托”给了 base

  • msg 属性被重写成了新的值。


实例代码:

val delegate = MyImplementation()
val delegatingObj = MyNewClass(delegate)
println(delegatingObj.msg)

输出:

Delegate sends regards.

但是,如果调用 print() 呢?

delegatingObj.print()

它会打印:

MyImplementation sends regards!

解释:
虽然 MyNewClass 没有自己写 print() 方法,但它委托给了 base,即 MyImplementation 的实例,调用的就是 MyImplementation 里的 print()。而 print() 内部打印的是 MyImplementationmsg,所以输出是 MyImplementation sends regards!

总结:

  • MyNewClass 中重写的成员会被使用。

  • 没有重写的方法和属性则直接委托给 base 实现。


一个更复杂的例子 — 回调和日志器

现在,我们看一个包含两个委托的复杂例子。

  • ICallbackReceiver:回调接口,支持在执行一个动作前后调用特定函数。

  • ILogger:日志接口,负责格式化并输出日志。

// 回调接口
interface ICallbackReceiver {
    fun onBeforeAction()
    fun onAfterAction()
    fun action(function: () -> Unit) {
        onBeforeAction()
        function()
        onAfterAction()
    }
}

// 日志接口
interface ILogger {
    fun getStubDateTime() = "05.11.2022-14:31:04" // 占位时间

    val format: String
        get() = "[${getStubDateTime()}]: "

    fun print(s: String)
}

实现:

// 简单的日志实现
class BasicLogger : ILogger {
    override fun print(s: String) = println(format + s)
}

// 实现回调接口,日志功能委托给 logger
class ConsoleNotifier(logger: ILogger) : ICallbackReceiver, ILogger by logger {
    val onBeforeStr = "OnBefore!"
    val onAfterStr = "OnAfter!"

    override fun onBeforeAction() = print(onBeforeStr)
    override fun onAfterAction() = print(onAfterStr)
}

再定义一个既支持回调又支持日志的类,全部用委托完成:

class ExampleParser(notifier: ICallbackReceiver, logger: ILogger) :
    ICallbackReceiver by notifier,
    ILogger by logger {

    fun start() = action { parseFiles() }

    fun parseFiles() {
        print("Parsing...")
        // 这里写具体的解析逻辑
    }
}

运行示例:

fun main() {
    val loggerInstance = BasicLogger()
    val dateTimeNotifier = ConsoleNotifier(loggerInstance)

    val simpleParser = ExampleParser(dateTimeNotifier, loggerInstance)
    simpleParser.start()
}

输出:

[05.11.2022-14:31:04]: OnBefore!
[05.11.2022-14:31:04]: Parsing...
[05.11.2022-14:31:04]: OnAfter!

总结

委托极大提升了代码复用的便利性。相比复制粘贴已有代码,我们只需引入已有的功能对象,并把接口的实现委托给它即可。Kotlin 对委托的语言支持非常好,让这项工作简单且优雅。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值