= Solution
Use <double dispatch>. Each original overload first sends a dynamically dispatched message to its argument. The argument's selected body then calls an overloaded receiver method with `this`, whose <static type> inside that body is the desired argument <software class>.
``
class X {
def sendTo(m: M): String = m.receive(this)
}
class Y extends X {
override def sendTo(m: M): String = m.receive(this)
}
class Z extends Y {
override def sendTo(m: M): String = m.receive(this)
}
class M {
def f(x: X): String = x.sendTo(this)
def f(x: Y): String = x.sendTo(this)
def receive(x: X): String = "f with X at M"
def receive(x: Y): String = "f with Y at M"
def receive(x: Z): String = "f with Z at M"
}
class N extends M {
override def f(x: Y): String = x.sendTo(this)
def f(x: Z): String = x.sendTo(this)
override def receive(x: X): String = "f with X at N"
override def receive(x: Y): String = "f with Y at N"
override def receive(x: Z): String = "f with Z at N"
}
``
All three `receive` overloads must be declared in `M`, because `sendTo` receives a parameter with <static type> `M`. Declaring the `Z` version only in `N` would repeat the original problem.
Whatever original `f` signature is selected, it invokes `sendTo` on the actual argument. For a `Z` object, <dynamic dispatch> enters `Z.sendTo`; inside that body, `this` has <static type> `Z`, so <method overloading> selects `receive(Z)`. The final call dynamically dispatches on the actual `m`, giving `N.receive(Z)` when `m` refers to `N`. Hence
``
f with Z at N
f with Z at N
f with Z at N
``
The result depends on both <runtime types>:
$$
\boxed{\text{argument }Z\text{ selects }receive(Z),\qquad\text{receiver }N\text{ selects its override}.}
$$
For the stated `X`, `Y`, `Z` hierarchy, the same construction reports each argument <software class> correctly for either receiver <software class>. Further subclasses would need their own dispatch protocol to distinguish additional <runtime types>.
Back to article page