ランタイムタイプではなく、宣言されたタイプによるこの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
タスクの方法がアクションの方法よりも選択されていることを非常にはっきりと見ることができます。しかし、オブジェクトは常にそれらが何であるかを常に知っているので、なぜ私は理由を理解していません、そして、私は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)
コードを変更して機能させることができますが、なぜ機能しないのかを理解したいと思います。助けてくれてありがとう!
解決
それがJavaでの派遣の仕組みです。
ディスパッチは最初に静的パラメータータイプに基づいており、静的署名が選択されると、メソッドを含むオブジェクトのランタイムタイプを使用して使用されます。
たとえば、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).
他のヒント
過負荷の方法を選択することは、コンパイル時に常に行われ、実行時にはありません。ランタイムの多型には、他の方法、つまり同一の署名を持つ方法を上書きする方法を選択することが含まれます。
合格したら Server
の中へ foo
また bar
, 、その呼び出しの範囲ではそれは Server
むしろa Runner
.
したがって、実行するとき delegate
メソッドの署名に従って、最も適切な一致に結合します。 delegate(Runner)
スコープされたパラメーターの危険なダウンキャストは必要ありません Server
.
このスコーピングは実行時に行われず、ソースコードの静的分析も維持することに注意してください。サーバーがあったことを覚えているだけです Server
それはあなたを混乱させています。その余分なスコープ外の知識なしでコードを分析すると、あなたはそれを見る delegate(Runner)
本当にあなたは有効な選択です。