首页 文章

Kotlin类委托覆盖的意外行为

提问于
浏览
3

根据我的理解, class 代表团应该这样做

允许对象组合实现与继承相同的代码重用 . [维基百科]

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

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

1 回答

  • 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 的调用只是委托给了 a ,并且 B 的类在 B 中的含义并不重要 .


    此外,如果我使用超级实现覆盖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 使用您的本地重写变量 .

    Update 1 (elaboration on second part)

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

相关问题