Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reflection Casting and Overloaded Method Dispatching in Java

Note that all the code is a simplified example in order to only communicate the core ideas of my question. It should all compile and run though, after slight editing.

I have several classes which all implement a common interface.

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

In a separate class I have a list of type Inter, which I use to store and remove Inter1 and Inter2 types, based on user input.

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

I also have a family of overloaded methods, which deal with how each implementation interacts with each other, along with a default implementation for 2 "Inter"s.

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");    
}

The methods are periodically called like so:

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());
    }
}

An example output from this is:

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

Looking at the output, it is clear that doSomething(Inter in1, Inter in2) is called, even in cases when other methods should be called. Interestingly, the class names outputted are the correct ones.

Why does java have static method overloading when the class types are determined at runtime using reflection? Is there any way to get Java to do this? I know I can use reflection and Class.getMethod() and method.invoke() to get the results I want, but it would be so much neater to do so with casting.

I realize that questions about similar concepts have been asked before, but while all of the answers were informative, none satisfied me. Double dispatch looked like it would work, but that would mean reworking a lot of code, since I use this type of thing often.

like image 267
Adno Avatar asked Mar 28 '12 03:03

Adno


1 Answers

It looks to me like we're talking about what's going on with:

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

Based on your surprise that the type that is being output is always Inter, it seems you're a little confused on what's going on here. In particular, you seem to think that in1.getClass().cast(in1) and in2.getClass().cast(in2) should be forcing a different overload because of their differing runtime type. However, this is wrong.

Method overload resolution happens statically. This means that it happens based on the declared types of the two arguments to the method. Since both in1 and in2 are both declared as Inter, the method chosen is obviously void doSomething(Inter in1, Inter in2).

The takeaway here is that in1 is declared as an Inter. This means that in1.getClass() is essentially the same as Inter.class for the purposes of static analysis -- getClass simply returns a Class<? extends Inter>. Therefore, the casts are useless, and you're only ever going to get the first overload.

like image 115
Kirk Woll Avatar answered Nov 11 '22 23:11

Kirk Woll