Overview of This Lesson

Method overloading allows you to begin to practice polymorphism. Polymorphism is having consistent behaviour between classes and objects that is appropriate. For example, think of the verb "open". You can go to your laptop and "open" it by lifting the lid. You can walk up to a door and "open" it by turning the handle and pushing or pulling it. You can go into a bank and "open" a bank account by providing personal information and some money to deposit. You can apply the verb "open" to many different kinds of objects, yet you always know how to "open" each object in the way it needs. The "open" action is performed in an appropriate way on several very different objects.

In programming, one way you can practice polymorphism is to create overloaded methods: methods that have the same name, but might perform in a different way depending on the type of arguments they receive and possibly even the type of data they return. The differences between these methods may be small, or they may be large.

For example, in using some of the existing classes in Java, you might have noticed that in the documentation, some classes have multiple versions of methods with the same name. For example, the Math class has four different versions of the max() method: one for two doubles, one for two floats, one for two ints, and one for two longs. Therefore, the Math.max() method is overloaded: each version operates on different kinds of numbers.

Similarly, there are multiple versions of the println() method. One version takes a string value, one version takes an integer value, and one version even takes a double value. That's why all of these statements work:

String str = "hi";
int intNum = 5;
double dblNum = 5.5;
System.out.println(str);
System.out.println(intNum);
System.out.println(dblNum);

In this lesson you'll learn how to create overloaded methods correctly and how they work.

Pre-Requisites

Before doing this lesson, make sure you understand how to create and use your own methods.

How to Create Overloaded Methods

To create overloaded methods, you simply declare more than one method with the same name and different parameter lists. The method names must all be the same and the parameter lists must be different. For example, the following two method headers define a set of overloaded methods called calculatePay():

public double calculatePay(int deptNum) { ... }
public double calculatePay(double hours, double rate) { ... }

When you invoke the calculatePay() method, Java will locate the correct method to execute based on the parameter list. So if you were to call the calculatePay() with the statement:

double pay = calculatePay(8.5, 24.50);

..then Java would locate the calculatePay() method that had two double parameter variables.

If you were to execute the statement:

double pay = calculatePay(2);

..then Java would locate the calculatePay() method that had one int parameter variable (or one double, since an int can be implicitly cast into a double).

If you were to try the statement:

double pay = calculatePay("Sales Department");

Then you would receive an error that the method calculatePay(java.lang.String) didn't exist: there is no calculatePay() method that takes a String.

You have to be careful to avoid ambiguous invocation when defining overloaded methods. Ambiguous invocation occurs when you invoke a method but there is more than one match for it. For example, the following overloaded methods would not be valid:

public static String findCustomer(String firstName) { ... }
public static String findCustomer(String lastName) { ... }

In the above example, Java sees two findCustomer() methods with a String parameter. The name of the parameter is irrelevant; Java sees these two method definitions as being exactly the same.

In general, you can overload methods as long as the parameter list is different. You can also change the return types, but again, only if the parameter lists are different. Therefore these methods are valid:

public static  int getValidNumber(String prompt, int min, int max) { ... }
public static double getValidNumber(String prompt, double max) { ... }

These are valid because the parameter list differs between the two. However, the methods below are not valid because even though the return types are different, the parameter lists are the same:

public static  int getValidNumber(String prompt, int min, int max) { ... }
public static double getValidNumber(String prompt, int min, int max) { ... }

So in summary, when creating overloaded methods, make sure that:

  1. The method names are the same.
  2. The parameter lists are of different.
  3. As long as Rule #2 applies, it's ok to have different return types.

Exercise

1. What is wrong with the following program?

public class Overloading {
    
    public static void main(String[] args) {
        doStuff("Sydney");
        doStuff("Sydney", 5);
    }
	
    public static void doStuff(String message) {
        System.out.println(message);
    }

    public static void doStuff(String message, int num) {
        for (int i=1; i<=num; i++)
            System.out.println(message);
    }

    public static boolean doStuff(String name) {
        return name.equalsIgnoreCase("kaluha")
    }
}

Ambiguous Invocation for the first and third doStuff() method: both accept a single String argument, so Java doesn't know which one to execute when you call doStuff("Sydney").

How Method Overloading Works

When you have multiple versions of the same method, how does Java know which method to use? It will always look for the best match. For example, imagine you had the following code segment:

int n1 = 5;
int n2 = 10;
double n3 = 1.0;
double n4 = 3.33;

System.out.println(Math.max(n1, n2));
System.out.println(Math.max(n3, n4));
        

In line 7, the max() method recieves 2 integers, therefore the integer version will execute. In line 8, the max() method receives two doubles, so the double version will execute. But what happens when you have two different types?

System.out.println(Math.max(n1, n3));

In the above example, we're passing one integer value and one double value. So will the 2-integers version execute or the 2-doubles version execute? If you were to execute that line of code, you would see that it outputs 5.0: n1 is 5 and n3 is 1.0, and the largest of those 2 is 5. The output showing a floating point literal tells you that the double version of Math.max() was executed. Why? Let's try a different example to understand the answer to that question.

public class OverloadingExample1 {
  public static void main(String[] args) throws Exception {
      
      int n1 = 5;
      int n2 = 10;
      double n3 = 1.0;
      double n4 = 3.33;

      // which one executes?
      method(n1, n2);
      method(n3, n4);
      method(n2, n4);
      method(n3, n2);
  }

  public static void method(int a, int b) {
      System.out.println("I take two integers.");
  }
  public static void method(double a, double b) {
      System.out.println("I take two doubles");
  }

  public static void method(int a, double b) {
      System.out.println("I take one int and one double.");
  }
}

Run the program and look at your output:

I take two integers.
I take two doubles
I take one int and one double.
I take two doubles

It should be obvious why the first two lines of output appear: the statement on line 10 executes method(int, int) because it receives the two integer arguments, and the statement on line 11 executes method(double, double) because it receives two double arguments. The statement on line 12 calls method() with one int and one double, which exactly matches the method(int, double) method. But what about the statement on line 13?

Line 13 calls method(n3, n2) - n3 is a double and n2 is an integer. But there is no method(double, int) at all. In this case, Java will try for the best match. It won't match method(int, int) or method(int, double) because it can't accept the double as the first argument. However, method(double, double) matches because the second int argument can be implicitly cast into a double. Therefore, the method(double, double) executes, which results in the fourth line of output.

Java will always choose the best match by matching up argument and parameter data types, performing implicit casting if necessary.

Knowing this, it confirms that Math.max(double, double) was executed since max(int, int), max(long, long), and max(float, float) can't match, but the first int argument can be cast into a double value for max(double, double).

Exercise

For each statement, identify which version of the overloaded methods will execute.

// main method:
printThings("hello", "world");
printThings("hello", 1.5);
printThings(5, 10);
printThings(5, 10.0);
printThings(1.0, 1.5);
  
// version 1
public static void printThings(String str1, String str2) {
    System.out.printf("%s and %s\n", str1, str2);
}
// version 2
public static void printThings(int num1, double num2) {
    System.out.printf("%d and %f\n", num1, num2);
}
// version 3
public static void printThings(int num1, int num2) {
    System.out.printf("%d and %d\n", num1, num2);
}
// version 4
public static void printThings(String str1, double num1) {
    System.out.printf("%s and %f\n", str1, num1);
}

printThings("hello", "world"); executes version 1 of printThings() because it takes 2 string arguments.

printThings("hello", 1); executes version 4, which accepts a string argument and a double argument - the int argument is implicitly cast into a double so that it can be passed to the method.

printThings(5, 10); executes version 3 of printThings(), because it takes 2 integer arguments.

printThings(5, 10.0); executes version 2 of printThings(), because it takes one integer and one double argument.

printThings(1.0, 1.5); doesn't match any of the methods: there is none that is capable of accepting two double values as arguments. This would appear in your code as an error that it "is not applicable for the arguments (double, double)".

Exercise

A program calculates and displays bonus amounts to pay various types of employees. There are 3 separate departments, numbered 1, 2, and 3. Department 1 employees are paid a bonus based on their sales: If their sales amount is over $5000 they get 5% of those sales, otherwise they get nothing. Department 2 employees are paid a bonus based on the number of units they sell: They get $20 per unit sold, and an extra $10 per unit if they sell 25 units or more; if they sell no units, they get nothing. Department 3 employees assemble parts in the plant and are paid a bonus of 10 cents per part if they reach a certain level: Part-time employees must assemble more than 250 parts to get the 10-cent-per-part bonus, and full-time employees must assemble more than 700.

Write a set of 3 overloaded methods called getBonus() that works with the program below, according to the specifications described above.

public final static int UNITS_PT = 250;
public final static int UNITS_FT = 700;
public final static double SALES_BONUS = 5000.0;
public final static double SALES_BONUS_RATE = 0.05;
public final static double SALES_UNIT_REG = 20.0;
public final static double SALES_UNIT_EXTRA = 10.0;
public final static int SALES_UNIT_BONUS = 25;
public final static double PARTS_BONUS = 0.1;

    public static void main(String[] args) {

    Scanner keysIn = new Scanner(System.in);
    System.out.println("Enter department: ");
    int dept = keysIn.nextInt();
    double bonus = 0;

    switch (dept)	{
        case 1:
            System.out.print("Enter sales: ");
            double sales = keysIn.nextDouble();
            bonus = getBonus(sales);
            break;
        case 2:
            System.out.print("Enter number of units sold: ");
            int numUnits = keysIn.nextInt();
            bonus = getBonus(numUnits);
            break;
        case 3:
            System.out.print("Enter # of pieces completed: ");
            int pieces = keysIn.nextInt();
            System.out.print("Full-time (1) or Part-Time (2)? ");
            int empType = keysIn.nextInt();
            int bonusLimit = (empType == 1) ? UNITS_FT : UNITS_PT;
            bonus = getBonus(pieces, bonusLimit);
            break;
        default:
            System.out.print("Error!  ");
	 }
	 System.out.printf("Bonus Amount: $%.2f%n", bonus);
}
Constants I added that are used in the solution
Solution to the getBonus() question