使用 Kotlin class 委托覆盖的意外行为
unexpected behaviour of override with Kotlin class delegation
据我了解,class 授权应该
allow object composition to achieve the same code reuse as
inheritance. [wikipedia]
Kotlin 支持 Class 委托,请注意 documentation 中的以下语句:
overrides work as you might expect: The compiler will use your
override implementations instead of those in the delegate object.
考虑到这一点,请考虑以下最小示例:
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"
}
我预计 B(AImpl()).printV()
会打印 B
,但它却打印 A
,即它使用 AImpl
.
的默认实现
此外,如果我使用 super
实现覆盖 B 中的 printV()
方法,即
class B(a: A) : A by a {
override val v: String = "B"
override fun printV() {
super.printV()
}
}
我现在预计 B(AImpl()).printV()
会打印 A
,但这次它会打印 B
。
这似乎违反直觉。
你能给这个行为一个很好的解释吗?
这按预期工作。
I expected that B(AImpl()).printV() would print B, however instead it
prints A, i.e. it uses the default implementation of AImpl.
总是想象 class 委托,因为您会自己将调用重定向到委托 class:
class B(private val a: A) : A {
override val v: String = "B"
override fun printV() {
a.printV()
}
}
这清楚地表明,对 printV
的调用只是委托给 a
而 v
的值在 [=50= 中并不重要] 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.
再一次想象一下委托在内部是如何工作的:
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
}
}
这清楚地表明,a
不再涉及并且 printV
使用您的本地覆盖变量。
更新 1(第二部分的详细说明)
https://kotlinlang.org/docs/reference/delegation.html
The Delegation pattern has proven to be a good alternative to
implementation inheritance
所以你不能把授权看成是继承。是委托(委托模式查维基百科)
... and the compiler will generate all the methods of Base that forward to b.
所以你接口的所有方法(v
-属性和printV
)都刚刚生成并转发给委托class。
这里是 class B
的代码片段和反编译的代码,看看它在内部是如何工作的:
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); }
}
据我了解,class 授权应该
allow object composition to achieve the same code reuse as inheritance. [wikipedia]
Kotlin 支持 Class 委托,请注意 documentation 中的以下语句:
overrides work as you might expect: The compiler will use your override implementations instead of those in the delegate object.
考虑到这一点,请考虑以下最小示例:
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"
}
我预计 B(AImpl()).printV()
会打印 B
,但它却打印 A
,即它使用 AImpl
.
此外,如果我使用 super
实现覆盖 B 中的 printV()
方法,即
class B(a: A) : A by a {
override val v: String = "B"
override fun printV() {
super.printV()
}
}
我现在预计 B(AImpl()).printV()
会打印 A
,但这次它会打印 B
。
这似乎违反直觉。
你能给这个行为一个很好的解释吗?
这按预期工作。
I expected that B(AImpl()).printV() would print B, however instead it prints A, i.e. it uses the default implementation of AImpl.
总是想象 class 委托,因为您会自己将调用重定向到委托 class:
class B(private val a: A) : A {
override val v: String = "B"
override fun printV() {
a.printV()
}
}
这清楚地表明,对 printV
的调用只是委托给 a
而 v
的值在 [=50= 中并不重要] 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.
再一次想象一下委托在内部是如何工作的:
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
}
}
这清楚地表明,a
不再涉及并且 printV
使用您的本地覆盖变量。
更新 1(第二部分的详细说明)
https://kotlinlang.org/docs/reference/delegation.html
The Delegation pattern has proven to be a good alternative to implementation inheritance
所以你不能把授权看成是继承。是委托(委托模式查维基百科)
... and the compiler will generate all the methods of Base that forward to b.
所以你接口的所有方法(v
-属性和printV
)都刚刚生成并转发给委托class。
这里是 class B
的代码片段和反编译的代码,看看它在内部是如何工作的:
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); }
}