When overriding an interface method implemented by class delegation, is it possible to call the class which is normally delegated to from within an overriding function? Similar to how you would call super
when using inheritance.
From the documentation:
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
class Derived(b: Base) : Base by b
fun main(args: Array<String>) {
val b = BaseImpl(10)
Derived(b).print() // prints 10
}
Note that overrides work as you might expect: The compiler will use your override implementations instead of those in the delegate object.
override fun print() { ... }
How to call the BaseImpl
print()
function from within this overridden function?
The use case is I want to add additional logic to this function, while reusing the existing implementation.
@Egor's answer works and should be suitable when the class delegated to is passed as a paremeter (dependency injection).
However, in my more specific use case (apologies for not having made this clear in my question) the implementation is directly defined in the delegation definition.
class Derived(b: Base) : Base by BaseImpl()
In this case Egor's answer does not work and a more elaborate approach is needed. The following implementation can be used in case you want to keep the implementation hidden so that callers can not modify it. For example, in my specific use case I am creating an aggregate root and want to protect internal invariants.
open class HiddenBaseImpl internal constructor( protected val _b: Base ) : Base by _b
class Derived() : HiddenBaseImpl( BaseImpl() )
{
override fun print()
{
_b.print()
... do something extra
}
}
Since HiddenBaseImpl
's primary constructor can only be used internally, callers of the library are unable to instantiate this class and are thus forced to use Derived
. Derived
can now call the class delegated to internally and add additional behavior without allowing callers to pass different implementations of Base
.
Since Base
is an interface, you can't really call super
on it (similar to Java).
Instead, you need to declare b
as a field and use it directly:
class Derived(val b: Base) : Base by b {
override fun print() {
b.print()
// ...
}
}
You can only use super
in Kotlin to access the superclass, no interfaces or other strange things (remember - Kotlin is running on the JVM). However, it is absolutely fine if you store the derivate instance in a variable, like Egor suggested in his answer.
To avoid that anybody can set or retrieve the variable, you can use a private (or protected, whatever suits your use case) primary constructor and add a second public constructor:
interface Base {
fun print()
}
class BaseImpl() : Base {
override fun print() { print(x) }
}
class Derived private constructor(private val b : Base) : Base by b {
constructor() : this(BaseImpl())
override fun print() = b.print()
}
fun main(args: Array<String>) {
val d = Derived()
d.b // does not work, compiler error
d.print() // prints 10
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With