Partial class delegation in Kotlin

6.8k views Asked by At

How do I partial delegate methods/fields in Kotlin?

To be specific: here I am trying to inherit class User from interface TraitA and implement field marked: Boolean in the wrapper StateA. That would clean up the User implementation, because marked is just a state field. Note that TraitA can't be a class because I want to use several such interfaces: User() : TraitA by StateA, TraitB by StateB, ..

/* does not compile (Kotlin M12) */
interface TraitA {
    var marked: Boolean

    fun doStaffWithMarked()  // must be overridable
}

class StateA() : TraitA {
    override var marked = false
}

class User() : TraitA by StateA(){
    override fum doStaffWithMarked() {
        //...all fancy logic here...
    }
}

The alternative is to implement all in one place:

class User() : TraitA{
    override var marked = false // ugly code

    override fum doStaffWithMarked() {
        //...
    }
}

Is there a way/pattern that would solve that problem with easy and as little code as possible? Code/bytecode generation is not an option for me.

UPDATE

I was not very clear about that, but please note that doStaffWithMarked() is unique for every User.

So I may suggest a 'half-bad' solution with run-time assertions:

interface TraitA {
    var marked: Boolean

    /* must be overridden */
    fun doStaffWithMarked() = throw UnsupportedOperationException()
}

class StateA() : TraitA {
    override var marked = false
}

class User() : TraitA by StateA() {
    override fum doStaffWithMarked() {
        //...all fancy logic here...
    }
}

The question is still open, since a really good solution would check that doStaffWithMarked() at compilation time.

2

There are 2 answers

2
Kirill Rakhman On BEST ANSWER

Split up TraitA into two interfaces, then delegate the one and implement the other:

interface TraitA {
    var marked: Boolean
}

interface TraitAPlus : TraitA {
    fun isMarked(): Boolean
}

class StateA() : TraitA {
    override var marked = false
}

class User() : TraitA by StateA(), TraitAPlus {
    override fun isMarked(): Boolean {
        return marked
    }
}
4
Kirill Rakhman On

Here's a version that just inherites from StateA instead of delegating but that's not very nice:

interface TraitA {
    var marked: Boolean

    fun isMarked(): Boolean
}

abstract class StateA() : TraitA {
    override var marked = false
}

class User() : TraitA, StateA() {
    override fun isMarked(): Boolean {
        return marked
    }
}

And here's a somewhat unusal approach where I delegate TraitA to an anonymous instance of StateA

class User() : TraitA by object : StateA() {
    override fun isMarked(): Boolean {
        return marked
    }
} {

}

To be honest though, I'd rather rethink the design of the class hierarchy instead. In particular, you can put method implementations in interfaces (but not property values) so if isMarked() only depends on marked you could just put the implementation for it directly in TraitA. Your code then becomes:

interface TraitA {
    var marked: Boolean

    fun isMarked(): Boolean {
        return marked
    }
}

class StateA() : TraitA {
    override var marked = false
}

class User() : TraitA by StateA() {

}

Edit: Separate answer: https://stackoverflow.com/a/30914383/615306