12

As to my understanding, the idea of delegating an implementation in Kotlin is to avoid code that looks like this:

class MyClass(val delegate : MyInterface) : MyInterface
{
    override fun myAbstractFun1() = delegate.myAbstractFun1()
    override fun myAbstractFun2() = delegate.myAbstractFun2()
    // ...
}

Instead, we can write the following code which should do the same:

class MyClass(val delegate : MyInterface) : MyInterface by delegate

Now, I'd like delegate to be a mutable variable, i.e. my code looks like this:

var delegate : MyInterface = MyImplementation()
object MyObject : MyInterface by delegate

So if I'd delegated every abstract method to delegate by myself like in the first example, changing the value of delegate does change the behaviour of the methods. However, the above code compiles to this Java code:

public final class MyObject implements MyInterface {
    public static final MyObject INSTANCE;
    // $FF: synthetic field
    private final MyInterface $$delegate_0 = MyObjectKt.access$getDelegate$p();

    @NotNull
    public String myAbstractFun1() {
        return this.$$delegate_0.myAbstractFun1();
    }

    @NotNull
    public String myAbstractFun2() {
        return this.$$delegate_0.myAbstractFun2();
    }
}

So obviously, instead of just using the delegate field, the Kotlin compiler decides to copy it when creating MyObject to a final field $$delegate_0, which is not modified when I change the value of delegate

Is there a better solution for doing this instead of delegating every method manually?

msrd0
  • 7,816
  • 9
  • 47
  • 82
  • What is the use case for this? – Todd Jan 02 '18 at 14:31
  • @Todd I'm trying to have a global object that holds the configuration of my library as well as providing methods to modify it. I know there are different ways to accomplish this, but this seems like a convenient way to have properties and methods in one place without creating a wrapper class – msrd0 Jan 03 '18 at 05:52
  • It doesn't even work if passing a proper `object`! It seems the compiler really, really want to know statically what's going to happen. I think that makes sense, too; if you implemented the methods yourself, they wouldn't change at runtime (unless reflection), so in order to have similar guarantees, the compiler needs to fix the delegate. – Raphael Oct 22 '18 at 16:51
  • @Raphael I don't see how that makes sense, as a manually implemented method can change bedaviour when the instance variables change. So why shouldn't that be the case for auto-generated methods as well? – msrd0 Oct 23 '18 at 10:45
  • @msrd0 A manually implemented method depends only on statically known declarations, and the compiler can reason about those (e.g. a `val` won't change). If you swap out the delegate -- potentially with a whole different class! -- what is _declared_ as a `val` (in the delegate interface) suddenly changed. (Note how you could never ever delegate `val`s!) Therefore, if you allowed to swap out delegate objects, the compiler can rely on (far?) fewer things. Now, that doesn't imply one _couldn't_ allow that; it's probably a trade-off. – Raphael Oct 23 '18 at 11:51
  • I think a discussion about design choices would be better off on https://discuss.kotlinlang.org/. – Raphael Oct 23 '18 at 11:52
  • 1
    See (and vote for it) https://youtrack.jetbrains.com/issue/KT-83 – PHPirate May 20 '21 at 14:51

1 Answers1

1

Sadly, as far as I know there is no way of changing the delegate by changing the original property content, but you might still be able to do something similar by working in an immutable way and copying the object:

interface MyInterface {
  fun foo():Int
}

data class MyClass(val delegate : MyInterface) : MyInterface by delegate

object ImplementationA: MyInterface { override fun foo() = 7 }
object ImplementationB: MyInterface { override fun foo() = 5 }

val objA = MyClass(ImplementationA)
println(objA.foo()) // returns 7

val objB = objA.copy(ImplementationB)
println(objB.foo()) // returns 5
println(objA.foo()) // still 7

Hope this is still useful.