为什么这个 Java 方法通过声明类型而不是运行时类型进行多态?

发布于 2024-10-22 10:35:50 字数 2007 浏览 2 评论 0原文

此代码:

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

我可以非常清楚地看到任务的方法正在选择而不是操作的方法。但我不明白为什么,因为对象总是知道它们是什么,而且我认为 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)

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

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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦途 2024-10-29 10:35:50

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

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).
太傻旳人生 2024-10-29 10:35:50

重载方法之间的选择总是在编译时进行,而不是在运行时进行。运行时多态性涉及在重写其他方法的方法之间进行选择——即具有相同签名的方法。

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.

甩你一脸翔 2024-10-29 10:35:50

因为一旦将 Server 传递到 foobar 中,在该调用的范围内它就不是 Server 而是而是一个Runner

因此,当您运行 delegate 时,它将根据方法的签名绑定到最合适的匹配项。 delegate(Runner) 不需要将作用域参数危险地向下转换为 Server

请注意,此范围界定不是在运行时完成的,它也适用于源代码的静态分析。只是您记得服务器是一个Server,这让您感到困惑。如果您在没有额外的超出范围的知识的情况下分析代码,那么您会发现 delegate(Runner) 确实是您唯一有效的选择。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文