Search code examples
kotlindelegation

Accessing the delegating receiver with by delegation in Kotlin


Given an interface:

interface Countable
{
    val count: Int
}

And an implementation/factory:

fun countable(counter: () -> Int): Countable = object : Countable
{
    override val count: Int
        get() = counter()
}

I can implement this using the class by delegation feature:

class CountableThing : Countable by countable({ 123 })

So that this snippet predictably outputs 123:

fun main()
{
    val countableThing = CountableThing()
    println(countableThing.count)
}

My question is, in the context of the delegate class, is there any way to get an instance of the delegating receiver?

In other words, can my delegate Countable implementation (the anonymous object defined in fun countable) see/access the receiver instance of the CountableThing class?

I tried this:

fun <T> countable(receiver: T, counter: () -> Int): Countable = object : Countable
{
    // ...
}

class CountableThing : Countable by countable<CountableThing>(this, { 123 })

But that's not valid, because expectedly:

class CountableThing : Countable by countable<CountableThing>(this, { 123 })
                                                             /^^^^
                        'this' is not defined in this context

Solution

  • No it can't, delegate objects are just objects they don't even know if they're going to be used to implement an interface through delegation. However, you can consider using delegated properties which are used to delegate properties setters and getters implementations:

    class Example {
        var p: String by Delegate()
    }
    
    class Delegate {
        operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
            return "$thisRef, thank you for delegating '${property.name}' to me!"
        }
    
        operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
            println("$value has been assigned to '${property.name}' in $thisRef.")
        }
    }
    

    then you can use

    val e = Example()
    println(e.p)
    

    which prints:

    Example@33a17727, thank you for delegating ‘p’ to me!
    

    As you can see, in your delegate implementation you can use thisRef which is a reference to the object whose property is been delegated.