Kotlin类委托覆盖的意外行为

时间:2017-10-23 09:23:50

标签: kotlin delegation

根据我的理解,班级代表团应该

  

允许对象组合实现与重用相同的代码   遗产。 [wikipedia]

Kotlin支持类委派,并在documentation

中注明以下声明
  

覆盖正如您所料的工作:编译器将使用您的   覆盖实现而不是委托对象中的实现。

考虑到这一点,请考虑以下最小例子:

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的默认实现。

此外,如果我使用printV()实现覆盖B中的super方法,即

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

我现在期望B(AImpl()).printV()会打印A,但这次会打印B。 这似乎违反直觉。

你能否解释一下这种行为?

1 个答案:

答案 0 :(得分:5)

这可以按预期工作。

  

我预计B(AImpl())。printV()会打印B,而不是它   打印A,即它使用AImpl的默认实现。

总是想象类授权,因为您将自己的调用重定向到委托类:

class B(private val a: A) : A {
    override val v: String = "B"

    override fun printV() {
        a.printV()
    }
}

这清楚地表明,对printV的调用刚刚委托给avB课程中的价值并不重要

  

此外,如果我使用super覆盖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 - 属性和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); }
}