kotlin - 使用 Kotlin 类委托(delegate)覆盖的意外行为

标签 kotlin delegation

据我了解,类(class)代表团应该

allow object composition to achieve the same code reuse as inheritance. [wikipedia]



Kotlin 支持类委托(delegate),请注意以下 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 的默认实现.

此外,如果我覆盖 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 .
这似乎违反直觉。

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

最佳答案

这按预期工作。

I expected that B(AImpl()).printV() would print B, however instead it prints A, i.e. it uses the default implementation of AImpl.



总是想象类委托(delegate),因为您会自己将调用重定向到委托(delegate)类:
class B(private val a: A) : A {
    override val v: String = "B"

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

这清楚地表明,对 printV 的调用刚刚委派给 a v 的值是多少并不重要在类(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.



同样,想象一下委托(delegate)在内部是如何工作的:
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



因此,您不能将委托(delegate)视为继承。它是委托(delegate)(在维基百科中查找委托(delegate)模式)

... and the compiler will generate all the methods of Base that forward to b.



因此,您的接口(interface)(v -property 和 printV )的所有方法都刚刚生成并转发到委托(delegate)类。

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

关于kotlin - 使用 Kotlin 类委托(delegate)覆盖的意外行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46885891/

相关文章:

android - Android 中的持续链式 react

android - 如何使接口(interface)的具体实现在库范围之外不可访问?

ios - Objective-C 中的委托(delegate)级联和劫持委托(delegate)回调

C++ 11 委托(delegate)构造函数纯虚方法和函数调用——危险?

java - Swing:将事件委托(delegate)给转换后的父组件的子组件

java - 如何触发 MINA 客户端消息事件?

unit-testing - 使用 PowerMock 在 Kotlin 中模拟包级函数

iphone - Objective-C 中类似 method_missing 的功能(即运行时的动态委托(delegate))

oop - 关于授权

android - ViewModel如何捕获上一层未引发的异常?