【问题标题】:This in kotlin interface delegation这在 kotlin 接口委托中
【发布时间】:2020-08-22 06:19:14
【问题描述】:

使用接口委托时有什么方法可以传递this?这将实现很好的可组合性 - 但我发现没有办法做到这一点。 意思是:

interface Foo {
}

class FooImpl(bar: Bar) : Foo {

}

class Bar: Foo by FooImpl(this) {
}

只要 FooImpl 不需要这样的参数,它就可以工作 - 但访问那里的其他类会很棒 - 也许有人知道一种方法。否则我也会感兴趣,如果这值得 KEEP,如果不值得 - 或者由于某种原因它是不可能的。

【问题讨论】:

  • 可能不可能,因为如果将其传递给 FooImpl,那么 FooImpl 的 init 块可能会访问任何属性或调用成员函数,而 Bar 类没有完全初始化并正在运行。而且您知道 Bar 的 init{} 块必须具有类实例 this,因此它必须在调用 Bar 的 init{} 之前已经实现 Foo。
  • 但我不完全确定将来是否有可能。

标签: kotlin interface delegation


【解决方案1】:

代表团不支持这一点。委托必须在委托给它的类之前实例化,因此委托不能依赖它进行构造。另一个问题是,尽管您可以覆盖委托的函数,但如果委托在内部调用这些函数,它将调用原始版本,而不是覆盖。代表确实生活在自己的世界中。

但是你可以设置它让宿主在它的初始化块中将自己传递给委托:

interface Foo<T> {
    var host: T
    fun doSomething()
}

class FooImpl : Foo<Bar> {
    override lateinit var host: Bar

    override fun doSomething() {
        println(host.name)
    }
}

class Bar(val name: String): Foo<Bar> by FooImpl() {
    init {
        host = this
    }
}

fun main() {
    val bar = Bar("Hello world")
    bar.doSomething()
}

不幸的是,这会使主机暴露于外部类与其自己的委托断开连接的可能性,但是。如果多次分配,也许您可​​以使该属性引发异常。

这是一个可以做到这一点的属性委托:

private class SingleAssignmentVar<T>: ReadWriteProperty<Any, T> {
    private var value: T? = null
    private var assigned: Boolean = false

    @Suppress("UNCHECKED_CAST")
    override fun getValue(thisRef: Any, property: KProperty<*>): T {
        if (!assigned)
            error("Property has not yet been set.")
        return value as T
    }

    override fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
        if (assigned)
            error("Property may only be set once.")
        assigned = true
        this.value = value
    }
}

fun <T> Delegates.singleAssignment(): ReadWriteProperty<Any, T> = SingleAssignmentVar()

【讨论】:

    【解决方案2】:

    您可以将 Bar 类分成两部分,例如后端和前端。

    前端将负责声明与委托的接口,后端将托管委托并充当组合目标。

    例如:

    interface Foo {
        fun sayHello(): String
    }
    
    class FooImpl(val bar: BarBackend) : Foo {
        override fun sayHello() = "Hello from ${bar.compositionTarget()}!"
    }
    
    class BarBackend() {
        val fooImpl: FooImpl = FooImpl(this)
    
        fun compositionTarget() = "backend"
    }
    
    class BarFrontend(backend: BarBackend) : Foo by backend.fooImpl
    
    fun main() {
        val bar = BarFrontend(BarBackend())
        println(bar.sayHello())
    }
    

    【讨论】:

    • 感谢您的回答 - 但这种模式对我不起作用,因为酒吧将是一个活动,我无法像这样创建它。应该在问题中指定这一点。会赞成但不能接受,因为我希望出现另一种模式。
    • 我对安卓不熟悉。但是默认参数值会来拯救吗?喜欢:class BarFrontend(backend: BarBackend = BarBackend()) : Foo by backend.fooImpl
    • 很遗憾没有。问题是您无法控制活动的构造函数
    猜你喜欢
    • 2021-02-20
    • 1970-01-01
    • 2018-03-05
    • 2019-09-08
    • 1970-01-01
    • 2012-07-05
    • 1970-01-01
    • 2014-08-18
    • 1970-01-01
    相关资源
    最近更新 更多