此代码:

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

我很清楚地看到,任务的方法是通过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)

我可以更改代码以使其正常工作,但是我想了解为什么它不起作用。谢谢您的帮助!

有帮助吗?

解决方案

这就是Dispatch在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).

其他提示

超载方法之间的选择始终是在编译时做出的,从不在运行时做出选择。运行时多态性涉及在覆盖其他方法的方法之间选择(即具有相同签名的方法)。

因为一旦你过去了 Server 进入 foo 或者 bar, ,在这个命令中,它不是 Server 而是一个 Runner.

因此,当您跑步时 delegate 根据该方法的签名,它将结合到最合适的匹配。 delegate(Runner) 不需要范围示意参数的危险降低 Server.

请注意,此范围不是在运行时完成的,它也将对源代码进行静态分析。只是您记得服务器是 Server 那让你困惑。如果您在没有额外范围的知识的情况下分析代码,那么您会看到 delegate(Runner) 真的是您只有有效的选择。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top