Почему этот метод Java полиморфируется по объявленному типу, а не по типу времени выполнения? - PullRequest
3 голосов
/ 16 марта 2011

Этот код:

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();
        }
    }
}

производит этот вывод:

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

Я очень хорошо вижу, что метод Task выбирается вместо метода Action. Однако я не понимаю почему, поскольку объекты всегда знают, что они из себя представляют, и я подумал, что выбор метода позднего связывания в Java сможет различать сигнатуры методов. Вызов bar() особенно запутан, так как task объявлен как Action в этой точке.

Если это имеет значение, то это 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)

Я могу изменить свой код, чтобы он работал, но я хотел бы понять, почему он не работает. Спасибо за помощь!

Ответы [ 3 ]

5 голосов
/ 16 марта 2011

Выбор между перегруженными методами всегда выполняется во время компиляции, а не во время выполнения. Полиморфизм времени исполнения включает в себя выбор между методами, которые переопределяют другие методы, то есть методы с одинаковыми сигнатурами.

5 голосов
/ 16 марта 2011

Вот как диспетчеризация работает в Java.

Диспетчеризация сначала основана на статических типах параметров, а после того, как статическая сигнатура была выбрана, тип времени выполнения объекта, содержащего метод, используется для выяснения, какойиспользуется переопределение.

Например, в

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).
1 голос
/ 17 марта 2011

Потому что, как только вы передадите Server в foo или bar, в рамках этого вызова это будет не Server, а скорее Runner.

Поэтому, когда вы запускаетеdelegate он будет привязан к наиболее подходящему совпадению в соответствии с сигнатурой метода.delegate(Runner) не требует опасного понижения параметра scoped в Server.

Обратите внимание, что это определение не выполняется во время выполнения, оно также выдержит статический анализ исходного кода.Просто вы помните, что сервер был Server, что вас смущает.Если вы проанализируете код без этих дополнительных знаний, то вы увидите, что delegate(Runner) на самом деле является единственным правильным выбором.

...