Java overloading rules
java, overloading
Solution
Overloading vs Overriding
The selection of the right implementation of method is done at runtime as you well pointed out, now the signature of the method to be invoked is decided at compile time.
Overloading Method Selection at Compile Time
The Java Language Specification (JLS) in section 15.12 Method Invocation Expressions explains in detail the process that the compiler follows to choose the right method to invoke.
There, you will notice that this is a compile-time task. The JLS says in subsection 15.12.2:
This step uses the name of the method and the types of the argument expressions to locate methods that are both accessible and applicable There may be more than one such method, in which case the most specific one is chosen.
Typically, varargs methods are the last chosen, if they compete with other candidate methods, because they are considered less specific than the ones receiving the same parameter type.
To verify the compile-time nature of this, you can do the following test.
Declare a class like this and compile it (i.e. `javac ChooseMethod.java`).
public class ChooseMethod {
public void doSomething(Number n){
System.out.println("Number");
}
}
Declare a second class that invokes a method of the first one and compile it (i.e. `javac MethodChooser.java`).
public class MethodChooser {
public static void main(String[] args) {
ChooseMethod m = new ChooseMethod();
m.doSomething(10);
}
}
If you run the program (i.e. `java MethodChooser`), the output says `Number`.
Now, add a second more specific overloaded method to the `ChooseMethod` class, and recompile it (but do not recompile the other class).
public void doSomething(Integer i) {
System.out.println("Integer");
}
If you run the main again, the output is still `Number`.
Basically, because it was decided at compile time. If you recompile the `MethodChooser` class (the one with the main), and run the program again, the output will be `Integer`.
As such, if you want to force the selection of one of the overloaded methods, the type of the arguments must correspond with the type of the parameters at compile time, and not only at run time.
Overriding Method Selection at Run time
Again, the signature of the method is decided at compile time, but the actual implementation is decided at runtime.
Declare a class like this and compile it.
public class ChooseMethodA {
public void doSomething(Number n){
System.out.println("Number A");
}
}
Then declare a second extending class and compile:
public class ChooseMethodB extends ChooseMethodA { }
And in the MethodChooser class you do:
public class MethodChooser {
public static void main(String[] args) {
ChooseMethodA m = new ChooseMethodB();
m.doSomething(10);
}
}
And if you run it you get the output `Number A`, and this is Ok, because the method has not been overridden in `ChooseMethodB` and therefore the implementation being invoked is that of `ChooseMethodA`.
Now, add an overridden method in `MethodChooserB`:
public void doSomething(Number n){
System.out.println("Number B");
}
And recompile just this one, and run the main method again.
Now, you get the output `Number B`
As such, the implementation was chosen at runtime, and no recompilation of the `MethodChooser` class was required.
Problem
I came across two overloading questions recently that I can't find an answer for and don't have a java environment to run some test code. I'm hoping someone can help me by assembling a list of all the rules that java compilers follow follow for overloading or alternately pointing me at a list that already exists. First, when two methods differ only by a final varargs parameter, under what circumstances do each get called and can you call the varargs method without any args? ``` private void f(int a) { /* ... */ } private void f(int a, int... b) { /* ... */ } f(12); // calls the former? I would expect it to f(12, (int[])null); // calls latter, but passes null for b? // Can I force the compiler to call the second method in the same fashion // as would happen if the first method didn't exist? ``` Second question, when two methods differ by types inherited from one another which gets called? I'd expect the most derived version to be called, and casting allowed to call the other. ``` interface A {} class B implements A {} class C implements A {} private void f(A a) {} private void f(B b) {} f(new C()); // calls the first method f(new B()); // calls the second method? f((A)(new B()); // calls the first method using a B object? ``` These are the two examples, but as a code-reader I'd prefer a canonical list of the exact ordered rules used for resolving this as I frequently don't have time to setup a build environment to check what the compiler is doing.