From what I understand, class delegation is supposed to
allow object composition to achieve the same code reuse as inheritance. [wikipedia]
Kotlin supports Class Delegation, and note the following statement form the documentation:
overrides work as you might expect: The compiler will use your override implementations instead of those in the delegate object.
With this in mind, consider the following minimal example:
interface A {
val v: String
fun printV() {
Logger.getLogger().info(Logger.APP, "A", v)
}
}
class AImpl : A {
override val v = "A"
}
class B(a: A) : A by a {
override val v: String = "B"
}
I expected that B(AImpl()).printV()
would print B
, however instead it prints A
, i.e. it uses the default implementation of AImpl
.
Moreover, if I override the printV()
Method in B using the super
implementation, i.e.
class B(a: A) : A by a {
override val v: String = "B"
override fun printV() {
super.printV()
}
}
I now expected that B(AImpl()).printV()
would print A
, however this time it prints B
.
This seems counterintuitive.
Can you give a good explanation for this behavior?
This works as expected.
I expected that B(AImpl()).printV() would print B, however instead it prints A, i.e. it uses the default implementation of AImpl.
Always imagine class delegation, as you would redirect the call to the delegation class by yourself:
class B(private val a: A) : A {
override val v: String = "B"
override fun printV() {
a.printV()
}
}
This makes clear, that a call to printV
is just delegated to a
and it doesn't really matter what the value of v
is in the class B
.
Moreover, if I override the printV() Method in B using the super implementation, i.e. I now expected that B(AImpl()).printV() would print A, however this time it prints B. This seems counterintuitive.
Again, imagine how the delegation works internally:
class B(private val a: A) : A {
override val v: String = "B"
override fun printV() {
super.printV() // the super call than uses the overridden v
}
}
This makes clear, that a
is not longer involved and printV
uses your local overridden variable.
Update 1 (elaboration on second part)
https://kotlinlang.org/docs/reference/delegation.html
The Delegation pattern has proven to be a good alternative to implementation inheritance
So you must not see delegation as inheritance. It is delegation (look up wikipedia for the delegation pattern)
... and the compiler will generate all the methods of Base that forward to b.
So all the methods of your interface (v
-property and printV
) are just generated and forwarded to the delegation class.
Here the code snippets of the class B
and the decompiled code to see how it works internally:
class B(a: A) : A by a {
override val v: String = "B"
}
class C(a: A) : A by a {
override val v: String = "B"
override fun printV() {
super.printV()
}
}
public final class B implements A {
@NotNull
private final String v = "B";
public B(@NotNull A a) {
this.$$delegate_0 = a;
this.v = "B";
}
@NotNull
public String getV() { return this.v; }
public void printV() {
this.$$delegate_0.printV();
}
}
public final class C implements A {
@NotNull
private final String v = "B";
public C(@NotNull A a) {
this.$$delegate_0 = a;
}
@NotNull
public String getV() {
return this.v;
}
/*This more or less means super.printV() */
public void printV() { A.DefaultImpls.printV(this); }
}
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