Thursday, June 18, 2009

Choosing the Most Specific Method - Tricky Overloading


Choosing the Most Specific Method - Tricky Method Overloading

Let's start with looking at a code-segment and try to think of the output/error, it would produce when compiled/executed and subsequently we'll discuss the behavior of code.


public class NullTest {

public static void method(Object obj){
System.out.println("method with param type - Object");
}

public static void method(String obj){
System.out.println("method with param type - String");
}

public static void main(String [] args){
method(null);
}
}


So, what do you expect as the output here? Before thinking about the output, do you really expect the code to compile successfully? Well... yeah, the code will compile and run fine as opposed to anyone who might have sensed an ambiguity here - we'll see the reason soon.

Since the methods are overloaded, the resolution will be done at compile-time only. Which method do you see being bind here - the one with parameter type 'Object' or the one with parameter type 'String' and why? Of course, the compiler can't bind two methods with one call, so on what basis would it pick the most suitable? Which method would be picked, is evident from the output given below:-


method with param type - String


Any guesses for why a special treatment is being given to 'String' here? Well... it's not actually for 'String' class specifically, but any sub-class would get a preference over the super class in such a situation. But, why? Because JLS (Section: 15.12.2.5) allows this. It clearly says:

"If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the run-time method dispatch. The Java programming language uses the rule that the most specific method is chosen."

As you easily deduce that the compiler should be able to pick 'the most specific', failing which it will throw a compile-time error. Let's understand it with the below code-segment which doesn't compile because the compiler can't pick 'the most specific' here.


public class NullTest {

public static void method(Object obj){
System.out.println("method with param type - Object");
}

public static void method(String str){
System.out.println("method with param type - String");
}

public static void method(StringBuffer strBuf){
System.out.println("method with param type - StringBuffer");
}

public static void main(String [] args){
method(null); //... compile-time error!
}
}


Why is the compiler not able to pick 'the most specific' here - because both String and StringBuffer are are sub-classes of the Object class, but without being in the same inheritance hierarchy. For finding 'the most specific' method, the compiler needs to find a method having the parameter type, which is a sub-class of the parameter types of all other overloaded methods.

This holds true for overloaded methods having more than one parameters as well. The compiler would pick 'the most specific' by looking which method is having at least one of its parameter types as a clear sub-class of the corresponding parameter type and other parameter types being either the same or clear sub-classes, in all other overloaded methods. If it can find one, good, otherwise it will throw a compile-time error. For example:


public class NullTest {

public static void method(Object obj, Object obj1){
System.out.println("method with param types - Object, Object");
}

public static void method(String str, Object obj){
System.out.println("method with param types - String, Object");
}

public static void main(String [] args){
method(null, null);
}
}

Output

method with param types - String, Object


In this case the compiler can easily pick 'the most specific' as the method having parameter types (String, Object) as the other overloaded method is having its parameter types as (Object, Object) - clearly 'String' is a subclass of 'Object' and the other parameter is of same type, so the method with parameter types (String, Object) can be picked with ease. But, the below code would throw a compile-time error as none of the methods satisfy the condition for being picked as 'the most specific' method.


public class NullTest {

public static void method(Object obj, String obj1){
System.out.println("method with param types - Object, String");
}

public static void method(String str, Object str1){
System.out.println("method with param types - String, Object");
}

public static void main(String [] args){
method(null, null); //... compile-time error!
}
}


Before I conclude let me thank Ranvijay (one of our regular visitors), who inquired about the underlying reason of this behavior via an email. I thought it would probably be helpful to other visitors as well and hence posted a full article. Keep contributing Ranvijay!

Liked the article? Subscribe to this blog for regular updates. Wanna follow it to tell the world that you enjoy GeekExplains? Please find the 'Followers' widget in the rightmost sidebar.



Share/Save/Bookmark


8 comments:

Ranvijay said...

hi geek,
thanks for such an excellent explaination.
i have a little bit confusion regarding to ResultSet

What does ResultSet actually contain? Is it the actual data of the result

or some links to databases? If it is the actual data then why can't we

access it after connection is closed?

Geek said...

ResultSet actually refers to a table containing the result of a SQL Query, so the answer to your first question is 'actual data'.

Why do you see a need of using a ResultSet once you have already closed the corresponding 'connection'? Or, was it just out of interest?

In case of a database session what happens when you closes the connection? This kind of answers your second question.

You might have thought that the ResultSet in Java is an object and hence it could be available when the connection goes down? Well... the JDBC APIs have been implemented in such a way that all the JDBC objects are closed when the corresponding 'connection' closes.

You might already be aware that a ResultSet object is automatically closed when the associated Statement object is closed, re-executed, or used to retrieve the next result from a sequence of multiple results.

Hope this helps. Keep posting!

Rushu said...

Thanks much for the explaination.

Could you please explain the behaviour for the below code snippet?

public class Test {
static void f(Object a, boolean b) {}
static void f(Object a, Object b) {}

static void m(int a, boolean b) { f(a,b); }
}

Geek said...

What exactly you want me to explain here? The first 'f' looks most specific (assuming 'int' is auto-boxed).

Dhaval said...

Thanx a lot for the help ...
actually i am about to give SCJP and m really happy to see some posts like these .. helps to clear the concept in greater detail ...

looking forward for more such nice blogs ...
u rock ;)

Geek said...

all the best for your SCJP exam!

ankit said...

Hi geek,

Thanks for you lovely explainations through out.... but here i did not understand one thing that how jvm would resolve when there are overloaded methods with int, double or string, ex
method(int i);
method(double i);
method(string i);

and you call method(1);

?

Anonymous said...

really too helpful..