ケース1、3、4はすでに理解しているので、ケース2に取り組みましょう。
(注意-私はJVMまたはコンパイラの内部動作の専門家ではありませんが、これが私が理解している方法です。これを読んでいる人がJVMの専門家である場合は、不一致があればこの回答を自由に編集してください。 。)
同じ名前で署名が異なるサブクラスのメソッドは、メソッドのオーバーロードと呼ばれます。メソッドのオーバーロードは静的バインディングを使用します。これは基本的に、コンパイル時に適切なメソッドが強制的に「選択」(つまりバインド)されることを意味します。コンパイラーは、オブジェクトの実行時タイプ(別名実際のタイプ)についての手がかりを持っていません。だからあなたが書くとき:
// Reference Type // Actual Type
Sub sub = new Sub(); // Sub Sub
Top top = sub; // Top Sub
コンパイラは、topがTop型(別名参照型)であることを「認識」しているだけです。したがって、後で書くとき:
System.out.println(top.f(str)); // Prints "subobj"
コンパイラは、呼び出し'top.f'をTopクラスのfメソッドを参照しているものとして「認識」します。strがObjectを拡張するString型であることを「知っています」。したがって、1)呼び出し'top.f'はトップクラスのfメソッドを参照するため、2)クラスTopにはStringパラメータを受け取るfメソッドがないため、3)strはObjectのサブクラスであるため、トップクラスのfメソッドです。コンパイル時に有効な唯一の選択肢です。したがって、コンパイラはstrをその親型であるObjectに暗黙的にアップキャストするため、Topのfメソッドに渡すことができます。(これは、上記のコード行の型解決が実行時まで延期され、コンパイラーではなくJVMによって解決される動的バインディングとは対照的です。)
次に、実行時に、上記のコード行で、topがJVMによって実際のタイプsubにダウンキャストされます。ただし、引数strは、コンパイラによってObject型にアップキャストされています。したがって、JVMは、Object型のパラメータを受け取るsubクラスのfメソッドを呼び出す必要があります。
したがって、上記のコード行は「sub」ではなく「subobj」を出力します。
別の非常によく似た例については、 Javaの動的バインディングとメソッドのオーバーライドを参照してください。
更新:JVMの内部動作に関するこの詳細な記事を見つけました:
http://www.artima.com/underthehood/invocationP.html
何が起こっているのかをより明確にするために、コードにコメントを付けました。
class Top {
public String f(Object o) {return "Top";}
}
class Sub extends Top {
public String f(String s) {return "Sub";} // Overloading = No dynamic binding
public String f(Object o) {return "SubObj";} // Overriding = Dynamic binding
}
public class Test {
public static void main(String[] args) {
// Reference Type Actual Type
Sub sub = new Sub(); // Sub Sub
Top top = sub; // Top Sub
String str = "Something"; // String String
Object obj = str; // Object String
// At Compile-Time: At Run-Time:
// Dynamic Binding
System.out.println(top.f(obj)); // Top.f (Object) --> Sub.f (Object)
// Dynamic Binding
System.out.println(top.f(str)); // Top.f (Object) --> Sub.f (Object)
// Static Binding
System.out.println(sub.f(obj)); // Sub.f (Object) Sub.f (Object)
// Static Binding
System.out.println(sub.f(str)); // Sub.f (String) Sub.f (String)
}
}