Javaメソッドのディスパッチは、ジェネリッククラスと抽象クラスでどのように機能しますか?
-
19-08-2019 - |
質問
今日、Javaが期待したメソッドを呼び出さないという状況に陥りました-最小限のテストケースを以下に示します: <!> quot;地獄はどうして that をするのでしょうか?<!> quot;立場から。)
これが起こる理由に特に興味があり、再設計の提案は気にしません。これはJava Puzzlersにあると感じていますが、コピーが手元にありません。
以下のTest <!> lt; T <!> gt; .getValue()内の称賛で特定の質問を参照してください:
public class Ol2 {
public static void main(String[] args) {
Test<Integer> t = new Test<Integer>() {
protected Integer value() { return 5; }
};
System.out.println(t.getValue());
}
}
abstract class Test<T> {
protected abstract T value();
public String getValue() {
// Why does this always invoke makeString(Object)?
// The type of value() is available at compile-time.
return Util.makeString(value());
}
}
class Util {
public static String makeString(Integer i){
return "int: "+i;
}
public static String makeString(Object o){
return "obj: "+o;
}
}
このコードからの出力は次のとおりです。
obj: 5
解決
いいえ、値のタイプはコンパイル時に使用できません。 javacは、可能なすべてのTに使用されるコードのコピーを1つだけコンパイルすることに注意してください。その場合、コンパイラがgetValue()メソッドで使用できる唯一のタイプはObjectです。
C ++は、最終的に必要に応じてコードの複数のコンパイル済みバージョンを作成するため、異なります。
他のヒント
使用するmakeString()
の決定はコンパイル時に行われるため、Tは何でもよいという事実に基づいて、Object
バージョンでなければなりません。考えてみてください。 Test<String>
を実行した場合、Test<T>
バージョンを呼び出す必要があります。そのため、makeString(Object)
のすべてのインスタンスは<=>を使用します。
次のようなことをした場合:
public abstract class Test<T extends Integer> {
...
}
状況は異なる場合があります。
Josh Blochの Effective Java には、ディスパッチがオーバーロードメソッドとオーバーライド(サブクラス)メソッドで異なるために生じる混乱を明確にする優れた議論があります。この質問の主題であるオーバーロードされたメソッドの選択は、コンパイル時に決定されます。 オーバーライドされたメソッドの選択は実行時に行われます(したがって、オブジェクトの特定のタイプの知識が得られます。)
この本は私のコメントよりもはるかに明確です。 <!> quot; Item 41:オーバーロードを慎重に使用する<!> quot;