Delegate property class constructors

There is one design decision with delegate properties I never understood.

The answer to this question by @kyay10 provided the solution, so now this post is being becomes a post for anyone else who has the same questions, rather than remaining as a question.

Why was this format (From the documentation):

class Example {
    var p: String by Delegate()
}
import kotlin.reflect.KProperty

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

chosen and not this format?

import kotlin.reflect.KProperty

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

Using this approach from other languages can be difficult to convert, as a table of delegate objects is required. If the table of delegate objects should contain the property names, and this becomes messy with the above syntax. That syntax does allow for the same instance of the Delegate class to be reused for multiple properties, but does not provide the delegate object itself with access to the parent object or the KProperty object.

I made a language design request to allow an alternate syntax supporting the Delegate constructor receiving the thisRef and property parameters.

In fact, as answered below, this already exists by way of the provideDelegate function.

1 Like

For your use case, you can define a provideDelegate operator so that you’ll have access to the thisRef and property at creation time. Assuming that your code is currently like this:

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

Then you can do this:

class Delegate(val initialValue: String, val thisRef: Any?, val property: KProperty<*>) {
    operator fun getValue(ignored: Any?, ignored2: KProperty<*>): String {
        return "$thisRef, thank you for delegating '${property.name}' to me! Your initial value was $initialValue."
    }
 
    operator fun setValue(ignored: Any?, ignored2: KProperty<*>, value: String) {
        println("$value has been assigned to '${property.name}' in $thisRef. Your initial value was $initialValue.")
    }
}
class DelegateProvider(val initialValue) {
    operator fun provideDelegate(thisRef: Any?, property: KProperty<*>): Delegate {
        return Delegate(initialValue, thisRef, property)
    }
}
// The use site will look like this:
class Example {
    var p: String by DelegateProvider("hello")
}
1 Like

Thank you… i had not used provideDelegate() and had missed that it was there!

I need to do some more work with my actual use case, but that certainly seems to pass the first hurdle.

1 Like

Please note that you can make getValue and setValue inline. In that case the new optimizer should even not generate the properties in use.

1 Like