Why there is no delegation by property

I wonder what is the reason of not implementing “delegation by property” which would be extremely powerfull language construct. Here is the illustration:

interface Interface
class InterfaceImpl: Interface

class Clazz: Interface by prop {
val prop = InterfaceImpl()
}

– compiler highlights prop in “by prop” as error
UPD I can define the prop in primary constructor but it is a significant limitation anyway

This can practically be done. See implementation by delegation in the official docs.

Here’s your example with the delegation as a runnable example:

//sampleStart
interface MyInterface

class MyImplementation : MyInterface

class Clazz : MyInterface by MyImplementation() {
    val prop: MyInterface = this
}
//sampleEnd
fun main() { 
    println("It compiles just fine") 
}

As you mentioned, you can use a constructor argument. Here’s your same example but with the constructor argument.

interface MyInterface

class MyImplementation : MyInterface
//sampleStart
class Clazz(val prop: MyInterface) : MyInterface by prop {
    // Prop is a member declared in constructor argument so it's available for delegation.
}
//sampleEnd
fun main() { 
    println("It compiles just fine") 
}

You could always use a factory or some other creation pattern to hide the creation of the MyImplementation delegate from the caller making it appear to the caller to be the same as the first solution.


It’s worth noting that this feature has been a point of debate and one of the least liked features of Kotlin (AFAIK the debate is mainly due to the idea that there may be more preferred alternatives to solve the same problems).

Thank you, I have figured out some of your answers myself after experimenting a little with different variants of syntax but anyway, what I have now is an ugly solution - I have to expose protected properties in primary constructor

I would recommend kotlin team making this language construct more flexible as it could be more powerfull - what is needed is kind of a postponed check in compiler - like a method can be used before its actual delcaration

Can you post an updated example of what you’re working with?

class Grid(parent: Container,
protected open val panel: Panel = Panel(parent),
protected open val basis: GridBasis = GridBasis(panel)) : RectUie by panel, GridBasisProps by basis

Okay so to make sure I’m understanding this correctly.
The following is your updated example but using the first solution of instantiating both panel and basis as delegates, which does not work because the second delegate needs to use the first.

interface Container
class Panel(obj: Any) : RectUie
val parent: Any = TODO()
class GridBasis(panel: Panel) : GridBasisProps
interface RectUie
interface GridBasisProps

/* Original:
class Grid(parent: Container,
protected open val panel: Panel = Panel(parent),
protected open val basis: GridBasis = GridBasis(panel)) : RectUie by panel, GridBasisProps by basis
*/

//sampleStart
class Grid(parent: Container) : 
    	RectUie by Panel(parent), 
    	GridBasisProps by GridBasis(panel /* this doesn't work! */ ) 
{
    // Private vars, but it failed to compile already.
    private val panel: Panel = (this as Panel) 
    private val basis: GridBasis = (this as GridBasis)
}
//sampleEnd

In this case, I’d recommend moving the construction logic into a factory and making the constructor private. Here’s one example where a factory function is made using Kotlin’s invoke operator so that it appears to the caller as a normal constructor (although the normal Java method of a static function or a full-blown stateful factory may be more useful depending on your use case).

interface Container
class MyContainer : Container
class Panel(obj: Any) : RectUie
class GridBasis(panel: Panel) : GridBasisProps
interface RectUie
interface GridBasisProps

/* Original:
class Grid(parent: Container,
protected open val panel: Panel = Panel(parent),
protected open val basis: GridBasis = GridBasis(panel)) : RectUie by panel, GridBasisProps by basis
*/

//sampleStart
//Notice private constructor that only cares about delegates, 
//we'll inject these dependencies instead of giving that responsibility to the `Grid` class.
// Note private `panel` and `basis` members.
class Grid private constructor(private val panel: Panel, private val basis: GridBasisProps) : 
    	RectUie by panel, 
    	GridBasisProps by basis
{
    
    // This static factory function is just one way. Plenty of other creation patterns to choose from.
    companion object {
        operator fun invoke(parent: Container) : Grid {
            val panel = Panel(parent)
            val basis = GridBasis(panel)
            return Grid(panel, basis)
        }
    }
}

fun main() {
    val parentContainer = MyContainer()
    
    // Notice how it looks like a constructor?
    // Everything is still private within the Grid class
    val myGrid: Grid = Grid(parentContainer)
}
//sampleEnd

You understood my problem correctly and thanks again for recommendations.

The problem is solvable of course, the point was rather to draw the attention of kotlin team to language limitations, which could be fixed pretty easily

1 Like