Kotlin: function delegation
Asked Answered
G

2

7

I have a project that depends heavily on delegation and composition in Kotlin. Delegating properties is a breeze, but conceptually I'm not completely sure how to achieve delegation for functions in circumstances where the functions depend on other composed properties. I'd like to do something like this:

interface A {
  val a: String
}
class AImpl: A {
  override val a = "a"
}
interface B {
  val b: String
}
class BImpl: B {
  override val b = "b"
}

interface C<T> where T: A, T: B {
  fun c() : String
}

class CImpl<T>(val ab: T) : C<T> where T: A, T: B {
  override fun c() = ab.a + ab.b
}

// works
class ABC : A by AImpl(), B by BImpl()

// does not work
class ABC : A by AImpl(), B by BImpl(), C<ABC> by CImpl(this)

Of course, this type of thing would be achievable with the following:

interface A {
  val a: String
}
class AImpl: A {
  override val a = "a"
}
interface B {
  val b: String
}
class BImpl: B {
  override val b = "b"
}

interface C<T> where T: A, T: B {
  fun c() : String
}

class CImpl<T>(val ab: T) : C<T> where T: A, T: B {
  override fun c() = ab.a + ab.b
}

class AB : A by AImpl(), B by BImpl()

class ABC(ab: AB = AB(), c: C<AB> = CImpl<AB>(ab)) : A by ab, B by ab, C<AB> by c

but this feels clunky as it requires passing in objects for composition which bloats the size of the constructors - it would be cleaner for me to initialize the objects at the site of the class itself as they have no use outside of the class. Is there an elegant way to this with delegation and/or extensions?

Geiss answered 25/6, 2017 at 10:52 Comment(4)
Good question. I tested this a little bit and found that you can drop the c parameter in your last example and just write this: class ABC(ab: AB = AB()) : A by ab, B by ab, C<AB> by CImpl<AB>(ab)Denby
Thanks for the suggestion! It's definitely a step in the right direction. It'd still be nice to have the constructor 100% argument-free, as I pass around constructor functions a lot in factory methods and it is cleaner if they all have the same signature.Geiss
it seems to an anti-pattern/abusing about delegation here. the code above try to delegates all of its neighbors. it lost the encapsulation benifit in oop. and I think this feature is never be published in kotlin since it will results in such a god class hard to use.Irvinirvine
I don't agree - imagine that there are delegated properties firstName and lastName and then we also wanted to delegate a function namePrinter that prints a formatted version of firstName and lastName. namePrinter could belong to an implementation class that encapsulates all sorts of private functions but fulfills the namePrinter contract and thus can be used as a delegate. the god class anti-pattern would be piling a bunch of namePrinter-like functions into one class, but it seems like using delegation would actually help avoid such an anti-pattern.Geiss
T
7

You can make C extend A and B instead of passing to it a delegate. e.g.:

interface C : A, B {
    fun c(): String
}

abstract class CImpl() : C {
    abstract override val a: String
    abstract override val b: String
    override fun c(): String = a + b
}

class ABC : A by AImpl(), B by BImpl(), CImpl()

You can also do this with a default implementation in C without a CImpl:

interface C : A, B {
    fun c(): String = a + b
}

class ABC : A by AImpl(), B by BImpl(), C
Typal answered 26/6, 2017 at 13:19 Comment(1)
This is really good stuff! Especially the second option as it is more composition-friendly.Geiss
H
0

I don't think this is currently supported very well, but there's an issue that tracks this and related feature requests. (See Peter Niederwieser's comment on the issue.)

Hermetic answered 25/6, 2017 at 15:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.