monk
monk

Reputation: 228

scala: overriding named parameter

Given the code below

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

The output is 21 but I think it should be 11. And when inspecting the compiled .class file, I found that parentRefToChild.mth(p1=1) was compiled to parentRefToChild.mth(1, parentRefToChild.mth$default$2()). How could scala compiler behave this way.

Upvotes: 2

Views: 375

Answers (1)

justAbit
justAbit

Reputation: 4256

Problem here is that you have changed sequence of parameters in Child:

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

So generated synthetic methods for mth in Child class, will be like:

def mth$default$1 = 10 // generated method for p2 in Child
def mth$default$2 = 20 // generated method for p1 in Child

When you call mth on Parent class reference, static type checking is used to determine if parameter has default value or not. Here, static type checking will be done on Parent as parentRefToChild is of type Parent.

So, when parentRefToChild.mth(p1=1) is encountered, at this point compiler does not know that parentRefToChild is actually holding Child class instance. It just tries to match mth's signature from Parent class. Now, here it sees that value of p1 is provided but p2 is missing and it has default value, it simply replaces parentRefToChild.mth(p1=1) with:

parentRefToChild.mth(1, parentRefToChild.mth$default$2())

Now at runtime, synthetic method mth$default$2() is picked from Child class (as parentRefToChild holds Child's instance) which then turns into:

parentRefToChild.mth(1, 20)

Hence you get 21 as output.

Upvotes: 5

Related Questions