为什么此Java方法通过声明类型而不是运行时类型进行多形化?
-
26-10-2019 - |
题
此代码:
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)
真的是您只有有效的选择。
不隶属于 StackOverflow