Method Overloading in Java

Several methods may share a name if their parameter lists differ, and the compiler decides which one runs before the program starts.

Definition

Overloading means declaring more than one method with the same name in the same class, distinguished by the number, types or order of their parameters.

Why it exists

Conceptually identical operations should read identically. Without overloading the standard library would need printlnInt, printlnDouble and printlnString, and every caller would carry that noise.

Example

public class Formatter {

    public String label(String name) {
        return name;
    }

    public String label(String name, int quantity) {
        return name + " x" + quantity;
    }

    public String label(String name, double price) {
        return name + " at " + price;
    }
}
Formatter f = new Formatter();
System.out.println(f.label("Pen"));          // Pen
System.out.println(f.label("Pen", 3));       // Pen x3
System.out.println(f.label("Pen", 12.50));   // Pen at 12.5

What counts as a difference

ChangeValid overload
Different number of parametersYes
Different parameter typesYes
Different order of distinct typesYes
Different return type onlyNo
Different parameter names onlyNo
Different throws clause onlyNo

Only the signature counts, and the signature is the name plus the ordered parameter types.

How the compiler chooses

Overload resolution happens at compile time, using the declared types of the arguments rather than the objects that turn up at runtime. The compiler works through three phases and stops at the first that finds a match:

  1. An exact match or a widening primitive conversion.
  2. Boxing or unboxing.
  3. A variable arity (varargs) parameter.
static void show(long value)    { System.out.println("long"); }
static void show(Integer value) { System.out.println("Integer"); }
static void show(int... values) { System.out.println("varargs"); }

show(5);   // prints "long" - widening is tried before boxing and varargs

This ordering exists for backward compatibility: code written before autoboxing had to keep behaving the same way.

Static type decides, not runtime type

static void print(Object o) { System.out.println("Object"); }
static void print(String s) { System.out.println("String"); }

Object value = "hello";
print(value);   // prints "Object", because the declared type is Object
This is the sharpest difference from overriding. Overloading is bound at compile time from declared types; overriding is bound at runtime from the actual object. Interviewers ask about it often, and the example above is the shortest way to show the difference.

Ambiguity

static void mix(int a, long b) { }
static void mix(long a, int b) { }

// mix(1, 2);   // compile error: neither is more specific than the other

Overloading constructors

public class Employee {

    private final String name;
    private final String department;

    public Employee(String name) {
        this(name, "Unassigned");     // delegate to the fuller constructor
    }

    public Employee(String name, String department) {
        this.name = name;
        this.department = department;
    }
}

Chaining with this(...) keeps the real initialisation in one place. It must be the first statement in the constructor.

Overloading compared with overriding

AspectOverloadingOverriding
WhereSame class, or inheritedSubclass replaces a superclass method
SignatureMust differMust match
BoundCompile timeRuntime
Return typeFreeSame or covariant
PurposeConvenience for callersPolymorphic behaviour

Common mistakes

  • Trying to overload on return type alone.
  • Overloading with Object and a specific type, then being surprised which one is chosen.
  • Mixing overloads and varargs so that adding an argument silently changes the method called.
  • Giving overloads different behaviour rather than different ways to express the same behaviour.

Best practices

  • Keep every overload semantically the same operation. Differing behaviour is a naming problem, not an overloading opportunity.
  • Let short overloads delegate to the fullest one, so the logic lives in a single method.
  • Avoid overloading on types that are related by inheritance or by boxing.
  • If resolution is not obvious to a reader, use distinct names instead.

Practice

  1. Predict the output of show(5) in the three overload example, then remove show(long) and predict again.
  2. Why does print(value) print Object even though the object is a String? How would you make it print String?
  3. Write four overloads of area for a square, rectangle, circle and triangle, and say whether that is a good use of overloading.
  4. Explain why two methods differing only in throws clauses will not compile.

Conclusion

Overloading is a compile time convenience that keeps related operations under one name. Use it when the operations really are the same idea, and avoid it when resolution would surprise a reader.

Written by Lorens Mishra

Default administrator account created by the installer.

Continue reading

All Java notes →
Java

Recursion in Java

A recursive method calls itself on a smaller problem, and it works only when a base case guarantees the shrinking stops.

Read more
Java

Varargs in Java

A variable arity parameter lets a method accept any number of arguments, and inside the method it is simply an array.

Read more
Java

Pass by Value in Java

Java always passes arguments by value. For objects the value copied is the reference, which explains every result that looks like pass by reference.

Read more
Java

Methods in Java

A method groups a piece of behaviour behind a name, a parameter list and a return type, and that signature is the contract callers depend on.

Read more

Discussion

0 comments
Sign in to join the discussion.

No comments yet. Be the first to say something.