7

私は今日、Javaが期待した方法を呼び出さなかった状況に遭遇しました-これが最小限のテストケースです:(これは不自然に思われるのが残念です-「現実世界」のシナリオはかなり複雑で、はるかに理にかなっています「どうしてそんなことをするの?」という観点から。)

私はこれがなぜ起こるのか特に興味があります、私は再設計の提案を気にしません。これはJavaパズルにあるような気がしますが、コピーが手元にありません。

以下のTest<T>.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
4

3 に答える 3

6

いいえ、値のタイプはコンパイル時に使用できません。javacは、考えられるすべてのTに使用されるコードのコピーを1つだけコンパイルすることに注意してください。その場合、コンパイラがgetValue()メソッドで使用できる唯一のタイプはObjectです。

C ++は、必要に応じて最終的に複数のコンパイル済みバージョンのコードを作成するため、異なります。

于 2009-01-17T01:41:18.113 に答える
2

makeString()を使用するかについての決定はコンパイル時に行われるため、Tは何でもかまいませんが、Objectバージョンである必要があります。考えてみてください。もしそうなら、それはバージョンTest<String>を呼び出さなければならないでしょう。Objectそのため、のすべてのインスタンスはTest<T>を使用しますmakeString(Object)

今、あなたが次のようなことをした場合:

public abstract class Test<T extends Integer> {
  ...
}

物事は異なる場合があります。

于 2009-01-17T01:42:33.460 に答える
2

Josh Bloch の「Effective Java」には、オーバーロードされたメソッドとオーバーライドされた (サブクラス内の) メソッドでディスパッチの動作が異なるために生じる混乱を明確にする優れた議論があります。オーバーロードされたメソッドの中からの選択(この質問の主題) は、コンパイル時に決定されます。オーバーライドされたメソッドの選択は実行時に行われます (したがって、オブジェクトの特定の型に関する情報が得られます)。

この本は私のコメントよりもはるかに明確です: 「項目 41: オーバーロードを慎重に使用する」を参照してください。

于 2011-07-29T21:48:01.910 に答える