The output is BADDCD, without a newline. Method overloading chooses a signature using static types, including the instantiated class type parameter; numeric widening can make a Double parameter applicable to an Int argument.
  • a.m(o1) chooses m(Int), which is more specific than the polymorphic method: B.
  • a.m(o2) uses m[Double](Double): A.
  • b.m(o1) compares m(Any) and m(Double). Int can widen to Double, and Double is more specific than Any: D.
  • b.m(o2) chooses m(Double) over m(Any): D.
  • c.m(o1) compares the instantiated m(Int) with m(Double) and chooses m(Int): C.
  • c.m(o2) can use only m(Double): D.
The third call is the subtle one: the existence of m(Any) does not prevent the more specific numeric overload from winning. These are Scala 2 method overloading and numeric widening rules. [ref]
The output is YXWX, without a newline. Apply overload selection followed by dynamic dispatch separately to each call:
  • c.m(a) selects the m(A) signature because the argument's static type is A. The receiver is a D at runtime, so its override prints Y.
  • d.m(b) selects m(B). D.m(B) explicitly calls the inherited implementation with super.m(o), giving X.
  • e.m(a) selects C.m(A), giving W.
  • e.m(b) selects C.m(B), giving X.
Although a actually refers to a B, that runtime type does not select another overload. Dynamic dispatch chooses the implementation of the already selected signature on the receiver.
Read each Scala trait linearization from the most specific implementation toward the base. For a declaration with parents , concatenate their linearizations from right to left, deleting a duplicate from a left operand when it already occurs in the right operand. This can move a repeated ancestor later than a superficial “rightmost trait first” scan suggests. Omitting the common AnyRef, Any suffix, the answers are
C, Y, H, N, L, I, E
B, Y, H, N, L, D, I, E
A, N, O, Y, S, H, T, L, D, I, E
For example, T contributes T,L,D,I,E, while O contributes O,Y,S,H,E. In A, the separately mentioned Y also appears in the later merge with O, and N's ancestors also occur in T; resolving these duplicates yields the final order displayed above. The inherited trait orders are the displayed lists after removing their leading class names. [ref]
Each implementation first evaluates super.meth and then appends its own name. Consequently the contributions appear in reverse order of the defining traits in the Scala trait linearization. The four printed lines are
E I L N H Y 
E I D L N H Y 
E I D L T H S Y O N 
E I D L T H S Y O N 
There is a trailing space before each newline. The third and fourth lines are identical: the declared type D of o4 permits the call but does not change the actual A object's dynamic dispatch or its super chain. Within N, for example, super.meth in an A follows the next implementation in A's full linearization rather than simply using L as its isolated textual parent.

Articles by others on the same topic (0)

There are currently no matching articles.