"Hello, Amigo! A couple of days ago I told you about overloading methods. Did you understand everything?"

"Yes. I remember. Each class method must be unique. A member method is unique if the class has no other method with the same name and parameter types (and the order of the parameters matters)."

"Very good! I see you've learned that lesson well. Today I want to expand your knowledge in this topic just a bit. What method do you think will be called in each case?"

Code
class Cat
{
 public static void print(int n)
 {
  System.out.println(n);
 }
 public static void print(short n)
 {
  System.out.println(n);
 }
 public static void print(Integer n)
 {
  System.out.println(n);
 }
 public static void print(String s)
 {
  System.out.println(s);
 }
public static void main(String[] args)
{
  Cat.print(1);
  Cat.print((byte)1);
  Cat.print("1");
  Cat.print(null);
 }
}

"It's hard to say."

"In the first case, 1 is an int. We have a 100% match with the method that takes an int. The first void print(int n). will be called.

In the second case, we don't have a method that takes a byte. But there are two methods that take a short and an int. Based on type widening rules, a byte will first be widened to a short, and then widened to an int. Thus, the verdict is that the void print(short n). will be called.

In the third case, we have a 100% match with the method that takes a String. The void print(String s). method will be called.

The fourth case is ambiguous. null doesn't have a specific type. The compiler will refuse to compile this code. In this case, we need to write Cat.print((Integer)null) to call the third method and Cat.print((String)null) to call the fourth."

"That was very informative. Thank you."

"I'd like to point out that when determining the correct method to call, types can only widen. They cannot narrow. Consider this example:"

Code
class Cat
{
 public static void print(short n)
 {
  System.out.println(n);
 }
 public static void print(Integer n)
 {
  System.out.println(n);
 }

 public static void main(String[] args)
 {
  Cat.print((byte)1);
  Cat.print(1);
 }
}

In the first case, the byte type will be widened to a short and the first method will be called: void print(short n)..

In the second case, there will be an implicit widening conversion from int to Integer, and then the second method will be called: void print(Integer n)..

"I didn't expect that."

"No, here's the real surprise:"

Java code Description
class Cat
{
 public static void print(Object o)
 {
  System.out.println(o);
 }
 public static void print(String s)
 {
  System.out.println(s);
 }

 public static void main(String[] args)
 {
  Cat.print(1);
  Cat.print(null);
 }
}
In the first case, int will be extended to Integer. Because there is no method for Integer, the most suitable method (and the one called) is void print(Object o)

In the second case, there won't be any compilation errors and void print(String s) will be called, which is somewhat not obvious.

"Amigo, I hope you understand that in such cases it's best to specify a type cast operator (as in we did with «(byte)») in order to know exactly which method will be called."

"I never expected any problems would come from overloading methods. But then you come along. Thanks, Rishi. I'll keep up my guard on this point."