Это в kotlin делегирование интерфейса - PullRequest
2 голосов
/ 07 мая 2020

Есть ли способ передать this при использовании делегирования интерфейса? Это обеспечило бы хорошую компоновку, но я не нашел способа сделать это. Означает что-то вроде:

interface Foo {
}

class FooImpl(bar: Bar) : Foo {

}

class Bar: Foo by FooImpl(this) {
}

, пока FooImpl не нуждается в таком параметре, как этот, он работает - но было бы здорово получить доступ к другому классу там - возможно, кто-то знает способ. В противном случае мне также было бы интересно, стоит ли это СОХРАНИТЬ, если нет - или если это будет невозможно по какой-то причине.

Ответы [ 2 ]

2 голосов
/ 07 мая 2020

Делегация этого не поддерживает. Делегат должен быть создан до класса, который ему делегирует, поэтому делегат не может полагаться на него для построения. Еще одна проблема заключается в том, что, хотя вы можете переопределить функции делегата, если делегат внутренне вызывает эти функции, он вызывает исходную версию, а не переопределение. Делегат действительно живет в своем собственном мире.

Но вы можете настроить его, чтобы хост передавал себя делегату в его блоке инициализации:

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()
1 голос
/ 07 мая 2020

Вы можете разделить класс Bar на две части, скажем, backend и frontend.

Frontend будет отвечать за объявление интерфейса с делегатами, backend будет размещать делегаты и действовать как цель композиции.

Например:

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