################## key points extracted ###########################
The rule for a method invocation that uses autoboxing/unboxing follows a two-step process.
If the actual argument being passed is a primitive type (as in test(10)),
Try to find a method with the primitive type argument. If there is no exact match, try widening the primitive type to find a match.
If the previous step fails, box the primitive type and try to find a match.
If the actual argument being passed is a reference type (as in test(new Integer(101)),
Try to find a method with the reference type argument. If there is match, call that method. In this case, a match does not have to be exact. It should follow the subtype and super type assignment rule.
If the previous step fails, unbox the reference type to the corresponding primitive type and try to find an exact match, or widen the primitive type and find a match.
#####################################################################
Overloaded Methods and Autoboxing/Unboxing
You have a few surprises when you call an overloaded method and want to rely on autoboxing/unboxing feature. Suppose you have two methods in a class.
public void test(Integer iObject) {
System.out.println("Integer=" + iObject);
}
public void test(int iValue) {
System.out.println("int=" + iValue);
}
Suppose you make two calls to the test() method.
test(101);
test(new Integer(101));
Which of the following will be the output?
int=101
Integer=101
or
Integer=101
int=101
The rule for a method invocation that uses autoboxing/unboxing follows a two-step process.
If the actual argument being passed is a primitive type (as in test(10)),
Try to find a method with the primitive type argument. If there is no exact match, try widening the primitive type to find a match.
If the previous step fails, box the primitive type and try to find a match.
If the actual argument being passed is a reference type (as in test(new Integer(101)),
Try to find a method with the reference type argument. If there is match, call that method. In this case, a match does not have to be exact. It should follow the subtype and super type assignment rule.
If the previous step fails, unbox the reference type to the corresponding primitive type and try to find an exact match, or widen the primitive type and find a match.
If you apply these rules to the above snippet of code, it will print
int=101
Integer=101
Suppose you have two test() methods.
public void test(Integer iObject) {
System.out.println("Integer=" + iObject);
}
public void test(long iValue) {
System.out.println("long=" + iValue);
}
What will be printed if you use the following code?
test(101);
test(new Integer(101));
It will print
long=101
Integer=101
The first call of test(101) will try to find an exact match for an int argument. It does not find a method test(int), so it widens the int data type, finds a match test(long), and calls this method.
Suppose you have two test() methods.
public void test(Long lObject) {
System.out.println("Long=" + lObject);
}
public void test(long lValue) {
System.out.println("long=" + lValue);
}
What will be printed if you execute the following code?
test(101);
test(new Integer(101));
It will print
long=101
long=101
Are you surprised by looking at the above output? Apply the rules that I have listed and you will find that the above output followed those rules. The call to test(101) is clear because it widens 101 from int to long and executes the test(long) method. To call test(new Integer(101)), it looks for a method test(Integer) and it does not find one. Note that a reference type is never widened. That is, an Integer is never widened to Long. Therefore, it unboxes the Integer to int and looks for test(int) method, which it does not find. Now, it widens the int and finds test(long) and executes it.
I have one more surprise. Consider the following two test() methods:
public void test(Long lObject) {
System.out.println("Long=" + lObject);
}
public void test(Object obj) {
System.out.println("Object=" + obj);
}
What will be printed when you execute the following code?
test(101);
test(new Integer(101));
This time, you will get the following output:
Object=101
Object=101
Does it make sense? Not really. Here is the explanation. When it calls test(101), it has to box int to an Integer, because there is no match for test(int), even after widening the int value. So, test(101) becomes test(Integer.valueOf(101)). Now it does not find any test(Integer) either. Note that Integer is a reference type and it inherits the Number class, which in turn inherits the Object class. Therefore, an Integer is always an Object, and Java allows you to assign an object of subtype (Integer) to a variable of supertype (Object). This is the reason that the test(Object) is called in this case. The second call, test(new Integer(101)), works the same way. It tries for test(Integer) method. When it does not find it, the next match for it is test(Object) based on the subtype and supertype assignment rule for reference types.