OCA Java SE 8 Method - Java Method Overload








Method overloading occurs when there are different method signatures with the same name but different type parameters.

System.out.println and StringBuilder's append methods provide many overloaded versions so you can pass just about anything to them.

In both of these examples, the only change was the type of the parameter. Overloading also allows different numbers of parameters.

Everything other than the method signature can vary for overloaded methods. This means there can be different access modifiers, specifiers (like static), return types, and exception lists.

These are all valid overloaded methods:

public void myMethod(int numMiles) { } 
public void myMethod(short numFeet) { } 
public boolean myMethod() { return false; } 
void myMethod(int numMiles, short numFeet) { } 
public void myMethod(short numFeet, int numMiles) throws Exception { } 

We can overload by changing anything in the parameter list. We can have a different type, more types, or the same types in a different order.

The access modifier and exception list are irrelevant to overloading.

Now let's look at an example that is not valid overloading:

public void myMethod(int numMiles) { } 
public int myMethod(int numMiles) { }     // DOES NOT COMPILE 

This method doesn't compile because it only differs from the original by return type.

public void myMethod(int numMiles) { } 
public static void myMethod(int numMiles) { }     // DOES NOT COMPILE 

The parameter list is the same. The only difference is that one is an instance method and one is a static method.





Overloading and Varargs

Which method do you think is called if we pass an int[]?

public void myMethod(int[] lengths) { } 
public void myMethod(int... lengths) { }     // DOES NOT COMPILE 

Java treats varargs as if they were an array. This means that the method signature is the same for both methods.

Since we are not allowed to overload methods with the same parameter list, this code doesn't compile.

Autoboxing

For the following method

public void myMethod(Integer numMiles) { } 

This means calling myMethod(3); will call the previous method as expected.

However, what happens if we have both a primitive and an integer version?

public void myMethod(int numMiles) { } 
public void myMethod(Integer numMiles) { } 

Java will match the int numMiles version. Java tries to use the most specific parameter list it can find.

When the primitive int version isn't present, it will autobox.





Reference Types

Java will try to choose the most specific version of a method, what do you think this code outputs?

public class Main { 
  public void myMethod(String s) { 
    System.out.print("string "); 
  } 

  public void myMethod(Object o) { 
    System.out.print("object "); 
  } 
  public static void main(String[] args) { 
    Main r = new Main(); 
    r.myMethod("test"); 
    r.myMethod(5); 
  } 
} 

The first call is a String and finds a direct match.

The second call looks for an int parameter list. When it doesn't find one, it autoboxes to Integer. Since it still doesn't find a match, it goes to the Object one.

Primitives

Primitives work in a way similar to reference variables. Java tries to find the most specific matching overloaded method. What do you think happens here?

public class Main { 
  public void myMethod(int i) { 
    System.out.print("int "); 
  } 
  public void myMethod(long l) { 
    System.out.print("long "); 
  } 
  public static void main(String[] args) { 
     Main p = new Main(); 
     p.myMethod(123); 
     p.myMethod(123L); 
  } 
} 

The answer is int long. The first call passes an int and sees an exact match.

The second call passes a long and also sees an exact match.

If we comment out the overloaded method with the int parameter list, the output becomes long long.

Java can only accept wider types. An int can be passed to a method taking a long parameter.

Java will not automatically convert to a narrower type. To pass a long to a method taking an int parameter, add a cast to explicitly say narrowing is okay.

Order

Order Java uses to choose the right overloaded method

RuleExample of what will be chosen for myMethod(1,2)
Exact match by typepublic String myMethod(int i, int j) {}
Larger primitive typepublic String myMethod(long i, long j) {}
Autoboxed typepublic String myMethod(Integer i, Integer j) {}
Varargspublic String myMethod(int... nums) {}

Example

Let's give this a practice run using the rules in Table 4.4. What do you think this outputs?

public class Main { 
  public static String print(String s) { 
    return "1"; 
  } 
  public static String print(String... s) { 
    return "2"; 
  } 
  public static String print(Object o) { 
    return "3"; 
  } 
  public static String print(String s, String t) { 
    return "4"; 
  } 
  public static void main(String[] args) { 
    System.out.print(print("a")); 
    System.out.print(print("a", "b")); 
    System.out.print(print("a", "b", "c")); 
  } 
} 

It prints out 142.

The first call matches the signature with single String because that is the most specific match.

The second call matches the signature with two String parameters since that is an exact match.

The third call matches the varargs version.

Example 2

The following code has compile error.

Java can convert the int 4 to a long 4 or an Integer 4. Java cannot convert int to a long and then to a Long.

public class Main { 
  public static void play(Long l) { } 
  public static void play(Long... l) { } 
  public static void main(String[] args) { 
    play(4);     // DOES NOT COMPILE 
    play(4L);     // calls the Long version 
  }
} 

If we had public static void play(Object o) { }, it would match because only one conversion would be necessary: from int to Integer. An Integer is an Object.