2012-03-28 107 views
4

请注意,所有代码都是一个简化示例,目的是仅传达我的问题的核心思想。它应该都经过轻微的编辑后编译并运行。使用Java进行反射投射和重载方法调度

我有几个类都实现了一个通用接口。

public interface Inter{} 
public class Inter1 implements Inter{} 
public class Inter2 implements Inter{} 

在一个单独的I类有类型间,我使用来存储和移除Inter1和Inter2类型,基于用户输入的列表。

java.util.ArrayList<Inter> inters = new java.util.ArrayList<Inter>(); 

我也有一个重载方法家族,它处理每个实现如何相互作用,以及2个“Inter”的默认实现。

void doSomething(Inter in1, Inter in2){ 
    System.out.println("Inter/Inter");  
} 
void doSomething(Inter1 in1, Inter1 in2){ 
    System.out.println("Inter1/Inter11");  
} 
void doSomething(Inter2 in1, Inter1 in2){ 
    System.out.println("Inter2/Inter1");  
} 

的方法是周期性地调用像这样:

for(int i = 0; i < inters.size() - 1; i++){ 
    for(int o = i+1; o < inters.size(); o++){ 
     Inter in1 = inters.get(i); Inter in2 = inters.get(o); 

     doSomething(in1.getClass().cast(in1), in2.getClass().cast(in2)); 

     System.out.println("Class 1: " + in1.getClass().getName()); 
     System.out.println("Class 2: " + in2.getClass().getName()); 
    } 
} 

从该输出的一个例子是:

Inter/Inter 
Class 1: Inter 
Class 2: Inter 
Inter/Inter 
Class 1: Inter 
Class 2: Inter1 
Inter/Inter 
Class 1: Inter1 
Class 2: Inter1 

综观输出,很显然,doSomething的(国际IN1, Inter in2)被调用,即使在应该调用其他方法的情况下也是如此。有趣的是,输出的类名是正确的。

为什么java在运行时使用反射来确定类类型时有静态方法重载? 有什么办法让Java来做到这一点?我知道我可以使用反射和Class.getMethod()和method.invoke()来获得我想要的结果,但是在使用cast的时候会更加简单。

我意识到之前已经提出过有关类似概念的问题,但所有的答案都是信息性的,没有人满意我。 Double dispatch看起来好像会起作用,但这意味着需要重新编写大量的代码,因为我经常使用这种类型的东西。

回答

6

它看起来对我来说,我们在谈论什么事情上:

doSomething(in1.getClass().cast(in1), in2.getClass().cast(in2)); 

根据您的惊喜,正在输出的类型总是Inter,看来你是一个有点困惑这里发生了什么。特别是,您似乎认为in1.getClass().cast(in1)in2.getClass().cast(in2)应该由于它们的运行时类型不同而导致不同的过载。但是,这是错误的。

方法重载分辨率静态地发生。这意味着它发生在方法的两个参数的声明类型的基础上。由于in1in2都被声明为Inter,所选的方法显然是void doSomething(Inter in1, Inter in2)

这里的外卖是in1被宣布为Inter。这意味着in1.getClass()与用于静态分析的Inter.class基本相同 - getClass只是返回Class<? extends Inter>。因此,演员阵容毫无用处,你只会得到第一个超载。

+0

谢谢,我希望不是这样,因为它相当烦人。我只是不禁觉得像这样的东西应该是动态的。它使一切变得更加复杂......噢,我想我只需要使用反射API,就像看起来那样混乱。 – Adno 2012-03-28 06:48:20

1

The Java Language Specification(JLS)in section 15。12 Method Invocation Expression详细解释了编译器按照选择正确方法调用的过程。

在那里,你会注意到这是一个编译时间任务。该JLS说,在第15.12.2:

此步骤使用方法和的类型参数的表达式 以找到既方便和适用 有可能会超过方法的名一种这样的方法,在这种情况下,选择最具体的一种。

就你而言,这意味着由于你传递了两个Integer类型的对象,所以最具体的方法就是接收这个对象的方法。

要验证此编译时间性质,您可以执行以下测试。

声明这样的类并编译它。

public class ChooseMethod { 
    public void doSomething(Number n){ 
    System.out.println("Number"); 
    } 
} 

声明第二个类,调用第一个类的方法并编译它。

public class MethodChooser { 
    public static void main(String[] args) { 
    ChooseMethod m = new ChooseMethod(); 
    m.doSomething(10); 
    } 
} 

如果您调用main,则输出为Number

现在,向ChooseMethod类添加更具体的第二种方法,然后重新编译它(但不要重新编译其他类)。

public void doSomething(Integer i) { 
System.out.println("Integer"); 
} 

如果再次运行main,输出仍为Number

基本上,因为它是在编译时决定的。如果您重新编译MethodChooser类(具有主类的类),并再次运行该程序,则输出将为Integer。因此,如果要强制选择其中一个重载方法,则参数的类型必须与编译时的参数类型一致,而不仅仅是在运行时,您似乎期望这个练习。