Kotlin班级代表团的意外行为

据我所知,班级代表团应该是这样的

允许对象组合实现与继承一样的代码重用。 [维基百科]

Kotlin支持类授权,并注意以下声明形式的文档 :

覆盖你所期望的工作:编译器将使用你的覆盖实现,而不是委托对象中的那些。

考虑到这一点,请考虑以下最简单的例子:

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 这似乎违反直觉。

你能给这个行为一个很好的解释吗?

    这按预期工作。

    我期望B(AImpl())。printV()会打印B,但是它打印A,即它使用AImpl的默认实现。

    总是想象班级代表团,因为你会自己把呼叫重定向到代表班级:

     class B(private val a: A) : A { override val v: String = "B" override fun printV() { a.printV() } } 

    这清楚地表明,对printV的调用只是被委托给a并且在类Bv的值并不重要。


    此外,如果我使用超级实现重写B中的printV()方法,即我现在预期B(AImpl()).printV()将打印A,但是这次打印出B,这似乎违反直觉。

    再次想象一下代表团如何在内部工作:

     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

    委托模式已被证明是实现继承的一个很好的选择

    所以你不能把代表团视为继承。 这是代表团(查看维基百科的代表团模式)

    …编译器将生成转发给b的Base的所有方法。

    所以你的界面( v -property和printV )的所有方法都只是生成并转发给了委托类。

    这里是类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); } }