Java方法调度如何与泛型和抽象类一起使用?

时间:2009-01-17 01:35:30

标签: java generics overloading

今天我遇到了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

3 个答案:

答案 0 :(得分:6)

不,编译时无法使用值的类型。请记住,javac只会编译一个代码副本,用于所有可能的T代码。鉴于此,编译器在getValue()方法中使用的唯一可能类型是Object。

C ++是不同的,因为它最终会根据需要创建代码的多个编译版本。

答案 1 :(得分:2)

因为在编译时决定使用makeString()是什么,并且根据T可能是什么的事实,必须是Object版本。想一想。如果您Test<String>,则必须调用Object版本。因此Test<T>的所有实例都将使用makeString(Object)

现在,如果你做了类似的事情:

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

事情可能会有所不同。

答案 2 :(得分:2)

Josh Bloch的 Effective Java 有一个很好的讨论,澄清了出现的混乱,因为dispatch对于重载vs overridden(在子类中)方法的工作方式不同。 重载方法中的选择---这个问题的主题---在编译时确定; 重写方法中的选择是在运行时完成的(因此可以了解对象的特定类型。)

这本书比我的评论更清楚:见 “第41项:明智地使用重载”