7

我今天遇到了一种情况,Java 没有调用我期望的方法——这是最小的测试用例:(很抱歉,这似乎是做作的——“现实世界”的场景要复杂得多,而且更有意义从“你为什么要这样做?”的角度来看。)

我对为什么会发生这种情况特别感兴趣,我不在乎重新设计的建议。我感觉这是在 Java Puzzlers 中,但我手边没有我的副本。

请参阅下面 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

No, the type of value is not available at compile time. Keep in mind that javac will only compile one copy of the code to be used for all possible T's. Given that, the only possible type for the compiler to use in your getValue() method is Object.

C++ is different, because it will eventually create multiple compiled versions of the code as needed.

于 2009-01-17T01:41:18.113 回答
2

Because the decision about what makeString() to use is made at compile-time and, based on the fact that T could be anything, must be the Object version. Think about it. If you did Test<String> it would have to call the Object version. As such all instances of Test<T> will use makeString(Object).

Now if you did something like:

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

things might be different.

于 2009-01-17T01:42:33.460 回答
2

Josh Bloch 的Effective Java有一个很好的讨论,澄清了由于重载和覆盖(在子类中)方法的分派工作方式不同而引起的混乱。重载方法的选择——这个问题的主题——是在编译时确定的;覆盖方法之间的选择是在运行时完成的(因此可以了解对象的特定类型。)

这本书比我的评论清楚得多:参见“第 41 条:明智地使用重载”

于 2011-07-29T21:48:01.910 回答