Method overloading chooses a signature using the static types available at the call site. The receiver
m has static type M, whose visible overloads accept X and Y; the Z overload introduced only in N is not a candidate.For Equivalently,The runtime type of every argument is
m.f(x), the argument's static type is X, so the chosen signature is f(X). N does not override it, and the inherited M body runs. For m.f(y), the chosen signature is f(Y), and dynamic dispatch on the receiver chooses N's override. For m.f(z), both visible signatures apply, but f(Y) is more specific than f(X). Dynamic dispatch again chooses N.f(Y). Thusf with X at M
f with Y at N
f with Y at NZ, but ordinary single-receiver dynamic dispatch does not redo method overloading using that runtime type. This is overload selection followed by dynamic dispatch.Apply overload selection followed by dynamic dispatch: the receiver's static type determines visible signatures, and argument static types choose the overload. The receiver's runtime type then selects its override. A
super call starts at the inherited implementation instead of redispatching to the override just entered.For
c.f1(y), the Y overload is visible on C, so its body in C runs. For a.f2(x), the X signature is selected and its override in C calls C.f1(X). For a.f2(y), the Y signature is selected; its body in C calls super.f1(y). The superclass B has only f1(X), so that call uses B.f1(X).For
b.f3(y), the static type B exposes only f3(X). Its override in C calls super.f2(x), entering B.f2(X). The unqualified f1(x) inside that body still uses dynamic dispatch on the original C instance, so it calls C.f1(X).Finally
c.f4(x) invokes the companion object C, which is a separate receiver extending B. Its f3(X) calls inherited B.f2(X), whose f1 call dispatches to ObjC.f1(X).[ref]Imperative Programming examination paper by the University of Oxford (2014). p. 10, Question 6(a), method bodies and calls
Apply overload selection followed by dynamic dispatch: method overloading chooses a signature from the receiver's static type and argument static types, then dynamic dispatch chooses an override of that signature using the receiver's runtime type. A
super call selects the inherited implementation; a call on the companion object B uses that object's own methods.[ref]Scala 2.11 expressions specification. Static overload resolution; this and super
The output, in order, isFor
f(x:X) in B
f(y:Y) in A
f(x:X) in A, g(x:X) in B
f(x:X) in ObjB, g(y:Y) in ObjB, h(y:Y) in B, h(z:Z) in C
f(y:Y) in C, m(a:A) in Y, k(x:X) in Ca.f(x), the argument's static type is X, selecting f(X); the receiver's runtime type B selects B.f(X). For a.f(z), the receiver's static type A exposes only f(X) and f(Y). The Z argument fits both, and f(Y) is more specific. B does not override that signature, so A.f(Y) runs. The additional overload B.f(Z) is invisible through a.For
c.g(z), the inherited instance B.g(X) calls super.f(x), which is A.f(X). For c.h(z), method overloading selects C.h(Z). Its super.h(z) calls B.h(Y), where the local parameter has static type Y. Consequently B.g(y) selects g(Y) on the companion object, whose only f signature is f(X).Finally,
c.k(x) calls x.m(this). The receiver x has runtime type Y, so dynamic dispatch enters Y.m(A). Inside that method, this has static type Y, selecting a.f(Y); the actual argument object is c, so dynamic dispatch reaches C.f(Y). ThereforeThe output is
YY, with no newline. The variable a has static type A, although its runtime type is B. Both c.m(a) and d.m(a) therefore select the signature m(A) at compile time. The m(B) alternative is not applicable to an argument whose static type is merely A.Both receivers refer to the same
D object. Dynamic dispatch then invokes D's override of the already selected m(A) signature, printing Y twice. The static type C of c changes the visible member set, but both signatures are visible through either receiver in this example. Overload selection followed by dynamic dispatch does not select an overload from the argument's runtime type.The output is
YXWX, without a newline. Apply overload selection followed by dynamic dispatch separately to each call: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.