鉴于以下代码

class Parent {
  def mth(p1: Int = 1, p2: Int = 2) = p1 + p2
}
class Child extends Parent{
  override def mth(p2: Int = 10, p1: Int = 20) = super.mth(p2, p1)
}
object Main {
  def main(args: String[]) = {
    val parentRefToChild: Parent = new Child
    println(parentRefToChild.mth(p1=1))   // 21
  }
}

输出是21,但我认为应该是11。在检查编译的.class文件时,我发现parentRefToChild.mth(p1=1)被编译为parentRefToChild.mth(1, parentRefToChild.mth$default$2())。 Scala编译器如何以这种方式表现。

最佳答案

这里的问题是您更改了Child中的参数顺序:

override def mth(p2: Int = 10 , p1: Int = 20)

因此,在mth类中为Child生成的综合方法将类似于:
def mth$default$1 = 10 // generated method for p2 in Child
def mth$default$2 = 20 // generated method for p1 in Child

当您在mth类引用上调用Parent时,将使用静态类型检查来确定参数是否具有默认值。在这里,静态类型检查将在Parent上进行,因为parentRefToChildParent类型。

因此,当遇到parentRefToChild.mth(p1=1)时,此时编译器不知道parentRefToChild实际上是在保存Child类实例。它只是尝试匹配mth类中的Parent的签名。现在,在这里看到提供了p1的值,但是p2丢失了,并且具有默认值,它只是将parentRefToChild.mth(p1=1)替换为:
parentRefToChild.mth(1, parentRefToChild.mth$default$2())
现在在运行时,从mth$default$2()类中选择合成方法Child(因为parentRefToChild持有Child的实例),然后将其转换为:
parentRefToChild.mth(1, 20)
因此,您得到21作为输出。

07-24 09:54