Java Overload Rules

Recently, I came across two overloaded questions: I can not find the answer and do not have a java environment to run some test code. I hope someone can help me by compiling a list of all the rules that java compilers follow to overload or alternately pointing to a list that already exists.

First, when the two methods differ only in the final parameter of varargs, under what circumstances does everyone call the call and you can call the varargs method without any arguments?

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? 

The second question is when the two methods differ in types inherited from each other, which are called? I would expect the most derived version to be called, and casting allowed to call another.

 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 two examples, but as a code finder, I would prefer a canonical list of exact ordered rules used to solve this issue, since I often do not have time to configure the build environment to check what the compiler does.

+4
source share
2 answers

Overload and Override

Choosing the right method implementation is done at runtime, as you well pointed out, now the signature of the method that is being called is determined at compile time.

Choosing an overload method at compile time

The Java Language Specification (JLS) in section 15.12 Method Invocation Expressions explain in detail the process that the compiler follows to select the correct method to invoke.

There you will notice that this is a compile-time task. JLS says in subsection 15.12.2:

This step uses the method name and types of argument expressions to find methods that are available and applicable. There may be more than one such method, in which case the most specific one is selected.

As a rule, varargs methods are the last if they compete with other candidate methods because they are considered less specific than those that receive the same type of parameter.

To check the nature of compilation time, you can do the following testing.

Declare a class like this and compile it.

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

Declare a second class that calls the method of the first and compile it.

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

If you call main, the output says Number .

Now add a second more specific overloaded method to the ChooseMethod class and recompile it (but don't recompile another class).

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

If you run main again, the output will be Number .

Basically, because it was decided at compile time. If you recompile the MethodChooser class (the one with the main one) and run the program again, the output will be Integer .

Thus, if you want to force one of the overloaded methods to be selected, the type of arguments must match the type of parameters at compile time, and not just at run time.

Choosing a Runtime Override Method

Again, the signature of the method is determined at compile time, but the actual implementation is determined 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 the second extension 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 will get the output of Number A , and that will be fine, because this method has not been overridden in ChooseMethodB , and therefore the implementation of ChooseMethodA .

Now add an override method to 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 of Number B

Thus, the implementation was chosen at runtime, and the recompilation of the MethodChooser class was not required.

+18
source

First question:

Your guess is correct. The second call to f() will call the varargs method. You can force the compiler to call the second method with:

 private void f(int a) { f(a, null); } 

Second question:

Yes. However, you cannot extend the interface. If you change A to an abstract class, everything will be compiled.

+1
source

All Articles