Question

This code:

public class PMTest
{

    private static class Runner { }
    private static class Server extends Runner { }

    private static class Task
    {
        public void delegate(Runner runner)
        {
            System.out.println("Task: " + runner.getClass().getName() +
                               " / " + this.getClass().getName());
        }
    }

    private static class Action extends Task
    {
        public void delegate(Server server)
        {
            System.out.println("Action: " + server.getClass().getName() +
                               " / " + this.getClass().getName());
        }
    }


    private static void foo(Task task, Runner runner)
    {
            task.delegate(runner);
    }

    private static void bar(Action task, Runner runner)
    {
            task.delegate(runner);
    }

    private static void baz(Action task, Server runner)
    {
            task.delegate(runner);
    }


    public static void main (String[] args)
    {
        try {
            Server server = new Server();
            Action action = new Action();

            action.delegate(server);
            foo(action, server);
            bar(action, server);
            baz(action, server);

        }
        catch (Throwable t) {
            t.printStackTrace();
        }
    }
}

produces this output:

$ java PMTest
Action: PMTest$Server / PMTest$Action
Task: PMTest$Server / PMTest$Action
Task: PMTest$Server / PMTest$Action
Action: PMTest$Server / PMTest$Action

I can see very clearly that Task's method is being chosen over Action's method. I don't understand why, though, since the objects always know what they are and I thought Java's late-binding method selection would be able to distinguish the difference in method signatures. The call to bar() is especially confusing, as task is declared as an Action at that point.

If it makes a difference, this is Java 6:

$ java -version
java version "1.6.0_14"
Java(TM) SE Runtime Environment (build 1.6.0_14-b08)
BEA JRockit(R) (build R27.6.5-32_o-121899-1.6.0_14-20091001-2113-linux-ia32, compiled mode)

I can change my code to make it work, but I'd like to understand why it doesn't work. Thanks for the help!

Was it helpful?

Solution

That's how dispatch works in java.

Dispatch is based first on static parameter types, and once a static signature has been chosen, then the runtime type of the object containing the method is used to figure out which overriding is used.

For example, in

void foo(Object o) {
  if (o instanceof Number) { foo((Number) o); }
  else if (o instanceof String) { foo((String) o); }
}

void foo(String s) { ... }

void foo(Number n) { ... }

{ foo((Object) "foo"); }  // Calls foo(Object) which calls foo(String).
{ foo("foo"); }  // Calls foo(String) without first calling foo(Object).

OTHER TIPS

The choice between overloaded methods is always made at compile time, never at runtime. Runtime polymorphism involves choosing between methods that override other methods -- i.e., methods with identical signatures.

Because once you pass Server into foo or bar, in the scope of that call it is not a Server but rather a Runner.

Therefore, when you run delegate it will bind to the most appropriate match, according to the method's signature. delegate(Runner) doesn't require the dangerous downcast of the scoped parameter into a Server.

Note that this scoping is not done at runtime, it will hold up to static analysis of the source code too. It's just that you remember server was a Server that's confusing you. If you analyse the code without that extra out-of-scope knowledge, then you'll see that delegate(Runner) really is you only valid choice.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top