JB Header

Method Overloading in Java with Examples

Introduction System.out.println() prints a String, an int, a double or a char - yet you always call it by the same name. That works because println() is overloaded: PrintStream has a separate println() method for each type. This tutorial explains method overloading in Java with examples. We will start with what overloading is and the rules for it. We will then overload methods by the number and by the types of parameters. Next we will see how the compiler chooses which overloaded method to call - including widening, boxing and varargs - and the ambiguous calls it rejects. Lastly, we will go through best practices.

(Note - This tutorial builds on Java Methods: Defining, Calling and Returning ValuesClick to read tutorial: Java Methods: Defining, Calling and Returning Values.) What is method overloading? Method overloading means defining several methods in the same class with the same name but different parameter lists. The compiler tells them apart by their signatures - the name plus the parameter types - and picks the right one for each call based on the arguments. The rules are -
  • Overloaded methods must differ in the number, the types, or the order of types of their parameters.
  • A different return type alone is not enough, and neither are different parameter names.
  • Overloaded methods may have different return types, access modifiers and static-ness, as long as the parameter lists differ.
Overloading is decided at compile time, which is why it is also called compile-time polymorphism. Overloading by number of parameters
Overloading by the number of parameters
//AreaCalculator.java
public class AreaCalculator {
  static double area(double side) {                      // square
    return side * side;
  }

  static double area(double length, double width) {      // rectangle
    return length * width;
  }

  static double area(double a, double b, double c) {     // triangle, from its three sides
    double s = (a + b + c) / 2;
    return Math.sqrt(s * (s - a) * (s - b) * (s - c));
  }

  public static void main(String[] args) {
    System.out.println("Square 4        : " + area(4));
    System.out.println("Rectangle 4 x 6 : " + area(4, 6));
    System.out.println("Triangle 3-4-5  : " + area(3, 4, 5));
  }
}
 OUTPUT of the above code
Square 4        : 16.0
Rectangle 4 x 6 : 24.0
Triangle 3-4-5  : 6.0
The three calls look alike, but each has a different number of arguments, so the compiler calls a different method each time. Without overloading, we would need three names, such as squareArea(), rectangleArea() and triangleArea().
Overloading by type and order of parameters
Overloading by parameter types
//Formatter.java
public class Formatter {
  static String describe(int value) {
    return "int " + value;
  }

  static String describe(double value) {
    return "double " + value;
  }

  static String describe(String value) {
    return "String \"" + value + "\"";
  }

  static String describe(String label, int count) {
    return label + " x" + count;
  }

  static String describe(int count, String label) {      // same types, different order
    return count + " of " + label;
  }

  public static void main(String[] args) {
    System.out.println(describe(42));
    System.out.println(describe(4.2));
    System.out.println(describe("Java"));
    System.out.println(describe("Books", 3));
    System.out.println(describe(3, "Books"));
  }
}
 OUTPUT of the above code
int 42
double 4.2
String "Java"
Books x3
3 of Books
Overloading by the order of types, as in the last two methods, is legal but easy to misuse - a caller who swaps two arguments silently gets a different method. Use it sparingly. What does not count as overloading Methods which differ only in their return type, or in parameter names, have the same signature - so declaring both is a compile error.
Methods that differ only in return type
//BadOverload.java
public class BadOverload {
  static int parse(String text) {
    return Integer.parseInt(text);
  }

  static double parse(String text) {
    return Double.parseDouble(text);
  }
}
 OUTPUT on compiling the above code
BadOverload.java:7: error: method parse(String) is already defined in class BadOverload
  static double parse(String text) {
                ^
1 error
If this were allowed, the compiler could not tell which method parse("42"); means - the return value is not even used. Give methods with different results different names, such as parseInt() and parseDouble().
How the compiler chooses an overloaded method When no method matches the argument types exactly, the compiler looks for the most specific method it can use, in three phases -
  1. Exact match or widening - an exact type match, or a widening conversionClick to read tutorial: Type Conversion and Casting in Java with Examples such as int to long or double. If several methods qualify, the one with the "narrowest" widening wins.
  2. Boxing and unboxing - only if phase 1 finds nothing, conversions such as int to Integer are allowed.
  3. Varargs - only if phases 1 and 2 find nothing, methods with variable-length arguments are considered.
Which overloaded method gets called?
//OverloadResolution.java
public class OverloadResolution {
  static void show(long x)       { System.out.println("show(long)      <- " + x); }
  static void show(double x)     { System.out.println("show(double)    <- " + x); }
  static void show(Integer x)    { System.out.println("show(Integer)   <- " + x); }
  static void show(Object x)     { System.out.println("show(Object)    <- " + x); }
  static void show(int... xs)    { System.out.println("show(int...)    <- " + xs.length + " values"); }

  public static void main(String[] args) {
    int i = 5;
    show(i);                       // int -> long wins over int -> double, and over boxing
    show(5.0f);                    // float -> double (widening)
    show(Integer.valueOf(7));      // exact match
    show("text");                  // String -> Object
    show('A');                     // char -> long (widening)
    show();                        // only varargs can take zero arguments
    show(1, 2, 3);                 // only varargs can take three
  }
}
 OUTPUT of the above code
show(long)      <- 5
show(double)    <- 5.0
show(Integer)   <- 7
show(Object)    <- text
show(long)      <- 65
show(int...)    <- 0 values
show(int...)    <- 3 values
Explanation of the code
  • show(i) with an int chose show(long). Widening (phase 1) beats boxing to Integer (phase 2), and long is a more specific widening of int than double.
  • show('A') also chose show(long), printing the character's code, 65 - a char widens to a number, not to a String.
  • The varargs method was used only when no other method could take the arguments. Varargs are covered in Java VarargsClick to read tutorial: Java Varargs: Variable-Length Arguments with Examples.
Ambiguous calls Sometimes two methods are equally specific, and the compiler cannot choose - a compile error.
An ambiguous overloaded call
//Ambiguous.java
public class Ambiguous {
  static void pay(int amount, double rate)  { }
  static void pay(double amount, int rate)  { }

  public static void main(String[] args) {
    pay(100, 5);
  }
}
 OUTPUT on compiling the above code
Ambiguous.java:7: error: reference to pay is ambiguous
    pay(100, 5);
    ^
  both method pay(int,double) in Ambiguous and method pay(double,int) in Ambiguous match
1 error
Both methods need one widening conversion for pay(100, 5), so neither is more specific. Calling pay(100, 5.0) would choose the first. The same error occurs when null is passed to methods taking, say, a String and an Integer. Best practices
  • Overloads should do the same thing: all methods with one name should perform the same operation on different inputs, like println() and Math.max().
  • Delegate to one main version: let simpler overloads call the most complete one with default values, so the logic lives in one place - for example greet(name) calling greet(name, "Hello").
  • Avoid confusing combinations: overloads with the same number of parameters whose types can convert into each other - int/long, int/Integer, or swapped orders - make it hard to know which one runs.
  • Overloading is not overriding: overriding is a subclass replacing an inherited method with the same signature, decided at runtime. See the @Override annotation tutorialClick to read tutorial on the @Override annotation.
Summary In this tutorial we first saw that method overloading means several methods with one name and different parameter lists, and went through the rules - a different return type alone is not enough. We then overloaded methods by the number of parameters, and by their types and order. Next we saw the error when methods differ only in return type. We then followed how the compiler chooses an overloaded method - exact match and widening first, then boxing, then varargs - and saw an ambiguous call. Lastly, we went through best practices, and the difference between overloading and overriding. In the next tutorial, we will write methods which accept any number of arguments with varargsClick to read tutorial: Java Varargs: Variable-Length Arguments with Examples.