【问题标题】:unexpected behaviour of override with Kotlin class delegation使用 Kotlin 类委托覆盖的意外行为
【发布时间】:2018-04-03 19:07:32
【问题描述】:

据我了解,类委派应该是

允许对象组合实现与相同的代码重用 遗产。 [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 的默认实现。

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

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

【问题讨论】:

    标签: kotlin delegation


    【解决方案1】:

    这按预期工作。

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

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

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

    这清楚地表明,对printV 的调用只是委托给av 在类B 中的值并不重要。


    此外,如果我使用 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

    委托模式已被证明是一个很好的替代方案 实现继承

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

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

    所以你接口的所有方法(v-property 和printV)只是生成并转发到委托类。

    这里是 B 类的代码 sn-ps 和反编译的代码,看看它内部是如何工作的:

    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); }
    }
    

    【讨论】:

    • 我刚说完前两句话,你就答完了。 =p 如果你使用this::class而不是v,第一个是AImpl,第二个是B
    • 我仍然不确定,如果我理解正确的话——我会期望委托更像继承。您能否详细说明第二部分关于 super 的内容?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-10-28
    • 2018-02-19
    • 2015-06-26
    • 1970-01-01
    • 2021-08-01
    • 1970-01-01
    • 2014-08-18
    相关资源
    最近更新 更多