Method overloading

System.out.println prints an int, a double, a String or a char, yet you never write printlnInt. That's method overloading: several methods share one name and differ only in their parameters. You'll see exactly how the compiler picks the right one, what makes a call ambiguous, and how real APIs use it.

Module 5 · Methods⏱ 20 minBeginner8 practice problems
1 The story

Three names for one idea

Riya's canteen app adds up a bill. She has written totalOfTwo(a, b), totalOfThree(a, b, c) and totalWithDiscount(a, b, percent). Her friend Kabir keeps calling the wrong one, and every new case needs a new name.

totalOfTwo(a, b) totalOfThree(a, b, c) totalWithDiscount(…) one name: total total(int a, int b) total(int a, int b, int c) total(int a, int b, double off) total(40, 25) total(40, 25, 15) total(40, 25, 0.1) javac picks by the arguments myphatshala.in
Riya

Sir, I keep inventing names: totalOfTwo, totalOfThree… Is there a better way?

Arjun Sir

Give them all the same name, total. Java allows several methods with one name as long as their parameter lists differ. The compiler looks at the arguments in each call and picks the matching version.

Riya

So when I write total(40, 25, 15), Java decides at run time which one to use?

Arjun Sir

No, earlier! The compiler decides, while compiling. By the time the program runs, each call already points to one exact method. You use this every day: println has ten versions.

2 Definition

What is method overloading?

Formal definition (write this in exams)

Method overloading means defining two or more methods in the same class with the same name but different parameter lists: a different number of parameters, different parameter types, or a different order of types. The method's signature (its name plus its parameter types) must be unique; the return type and parameter names are not part of it. The compiler chooses which overloaded method to call from the static types of the arguments, so overloading is a form of compile-time (static) polymorphism.

In simple words

Same name, different inputs. You call total(…); Java looks at what you passed in and runs the version written for exactly those inputs.

Words you'll meet in this lesson

OverloadingSeveral methods with one name and different parameter lists.
SignatureMethod name + parameter types, e.g. total(int, int). Must be unique in a class.
Overload resolutionThe compiler's process of choosing one method for a call.
WideningAutomatic conversion to a bigger type: int → long → float → double.
BoxingAutomatic conversion of a primitive to its wrapper: int → Integer.
Ambiguous callTwo methods fit equally well, so javac refuses to choose.

What counts as a different parameter list?

ChangeExample pairValid overload?
Number of parametersarea(int) and area(int, int)Yes
Type of parametersarea(int) and area(double)Yes
Order of parameter typespay(int, String) and pay(String, int)Yes (but confusing; avoid)
Only the return typeint calc(int) and double calc(int)No: method calc(int) is already defined
Only parameter namesfee(int days) and fee(int hours)No: same signature
Only static or access modifierstatic void log(String) and void log(String)No: same signature

Exam tip: “Can we overload by return type?” No. A call like calc(5); doesn't have to use the result, so the compiler couldn't tell the two apart. Overloading is compile-time polymorphism; overriding (Lesson 7.4) is run-time polymorphism.

3 Types and ways to use it

Three ways to overload, and where you've already seen them

By number of parameters

total(a, b) and total(a, b, c). The most common kind, often used for optional extras.

String.format, ArrayList(…)

By type of parameters

print(int), print(double), print(String). Same idea, different kinds of data.

println, Math.abs, String.valueOf

By order of types

book(int seats, String show) and book(String show, int seats). Legal, but easy to misuse: prefer different names.

legal, rarely wise

The JDK is full of overloads

public class Main {
    public static void main(String[] args) {
        System.out.println(42);            // println(int)
        System.out.println(4.5);           // println(double)
        System.out.println('A');           // println(char)
        System.out.println("Chai");        // println(String)
        System.out.println(Math.abs(-7));    // abs(int)    returns int
        System.out.println(Math.abs(-7.5));  // abs(double) returns double
        System.out.println(String.valueOf(true) + String.valueOf(99));
    }
}
42 4.5 A Chai 7 7.5 true99

Each line calls a different method that happens to share a name. Notice that the two Math.abs methods also return different types. That's allowed, because their parameters differ too.

Constructors can be overloaded too

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        ArrayList<String> a = new ArrayList<>();      // ArrayList()
        ArrayList<String> b = new ArrayList<>(500);   // ArrayList(int initialCapacity)
        StringBuilder s1 = new StringBuilder();          // StringBuilder()
        StringBuilder s2 = new StringBuilder("Bill: ");  // StringBuilder(String)
        System.out.println(a.size() + " " + b.size() + " [" + s1 + "] [" + s2 + "]");
    }
}
0 0 [] [Bill: ]

Good practice: overloads should all do the same job. A common pattern is for the short versions to call the full one: total(a, b) just returns total(a, b, 0). Then the real logic lives in one place.

4 Step by step

How javac chooses one method for a call

  1. Collect every method with that name

    static void show(long x)    { System.out.println("long"); }
    static void show(Integer x) { System.out.println("Integer"); }
    static void show(int... x)  { System.out.println("varargs"); }
    
    show(5);   // which one?
    javac finds the three show methods in the class. Methods with a different name are never considered.
  2. Phase 1: exact match or widening only

    The argument 5 is an int. No show(int) exists, but int can widen to long. So show(long) is applicable in phase 1, and javac stops here: it prints long. Boxing and varargs are not even tried.
  3. Phase 2: allow boxing and unboxing

    Only if phase 1 finds nothing. Remove show(long) and the call boxes 5 into an Integer: it prints Integer.
  4. Phase 3: allow varargs

    Last resort. Remove the first two and show(int...) is used: the JVM receives a new int[]{5}. Varargs come in Lesson 5.6.
  5. Several fit in the same phase? Pick the most specific, or fail

    Main.java:6: error: reference to add is ambiguous
        add(1, 1);
        ^
      both method add(int,long) in Main and method add(long,int) in Main match
    If one candidate's parameters are all “narrower” than another's, it wins (String beats Object; int beats long). Here each method is better for one argument and worse for the other, so neither is more specific: compile error.
  6. The choice is written into the bytecode

    invokestatic  #33   // Method area:(I)I
    invokestatic  #42   // Method area:(II)I
    invokestatic  #47   // Method area:(D)D
    Each call instruction names the method and its descriptor: (I)I means “takes an int, returns an int”, (D)D double to double. The JVM never has to choose; javac already did.
5 See it happen

Three calls, three different methods

Left: the call you write and what it prints. Right: how javac matches the arguments to a signature and what it writes into the class file.

Overload resolution for area(…) 6 steps
What you see area(5) area(4, 6) area(2.5) area('A') // a char! area(5L) // a long int calc(int) + double calc(int) int area(int side) int area(int l, int w) double area(double r) 25 24 19.625 4225 78.5 error: method calc(int) is already defined in class Main One int argument → area(int). Two ints → the two-parameter version. A double → area(double). 'A' is 65: char widens to int → 65 × 65. long can't narrow to int, but widens to double. Same parameters, different return type: not allowed. myphatshala.in
Behind the screen javac: argument types vs signatures argument types: (int) argument types: (int, int) argument types: (double) argument types: (char) argument types: (long) two methods: calc(int), calc(int) area(int) area(int,int) area(double) ✓ exact✗ 2 params✓ by widening (worse) ✗ 1 param✓ exact✗ 1 param ✗ double → int narrows✗ 2 params✓ exact ✓ char → int widens✗ 2 params✓ but less specific ✗ long → int narrows✗ 2 params✓ long → double widens Signatures are identical: calc(int) The return type isn't part of the signature. bytecode: invokestatic area:(I)I invokestatic area:(II)I invokestatic area:(D)D The exact match is the most specific. Only one method has two parameters. double never narrows to int silently. In bytecode a char is already an int. The argument is widened to 5.0 before the call. No class file is written. myphatshala.in
6 Real-world use

Where overloading shows up in real code

Overloading is how good APIs stay easy to use: one name to remember, and sensible defaults for the details you don't care about.

Watch it with an example: one pay() name, three ways to pay

A canteen app has three pay methods. Each call below has different arguments, so javac connects it to a different method before the app ever runs.

pay(…) calls matched to overloads 5 steps
Animated example pay(int amount) → cash payment pay(int amount, String upiId) → UPI payment pay(int amount, String card, int cvv) → card payment pay(60) pay(60, "canteen@upi") pay(60, "4111…", 123) pay("60") pay(60, 123) ✗ no match String ≠ int int ≠ String compile error myphatshala.in
println(int|String…

1. Printing and converting: println, valueOf

PrintStream.println has versions for every primitive, char[], String and Object. String.valueOf works the same way. You write one name; the right conversion happens.

Any value→println(…) overloads→Text on screen

2. Optional details with defaults

Logging libraries offer log(message) and log(message, exception). HTTP clients offer get(url) and get(url, headers). The short versions call the long one with default values.

log("Saved")→log(msg, null)→One real implementation

3. Different ways to build an object

Constructors are overloaded constantly: new ArrayList<>(), new ArrayList<>(500), new ArrayList<>(otherList). A booking class might accept a seat number or a seat code.

Different inputs→overloaded constructors→Same kind of object

4. Same operation, different data types

Math.max, Math.abs and Arrays.sort each have versions for int, long, float, double (and more), so each type gets fast code without boxing.

int[] or double[]→Arrays.sort overloads→Type-specific sorting

Examples are simplified. Library designers keep overload sets small and consistent; when two overloads would behave differently, they choose different method names instead.

7 Run the code

Riya's bill calculator with overloads

public class Main {
    static int total(int a, int b) {
        return total(a, b, 0);
    }

    static int total(int a, int b, int c) {
        return a + b + c;
    }

    static double total(int a, int b, double discount) {
        return (a + b) * (1 - discount);
    }

    public static void main(String[] args) {
        System.out.println("Tea + Vada: " + total(10, 25));
        System.out.println("Tea + Vada + Samosa: " + total(10, 25, 15));
        System.out.println("With 10% off: " + total(40, 60, 0.10));
    }
}
Tea + Vada: 35 Tea + Vada + Samosa: 50 With 10% off: 90.0
  • total(int a, int b)The short version doesn't repeat the logic: it calls the three-int version with 0.
  • total(int, int, int) vs total(int, int, double)Same count, different type of the third parameter: a valid overload.
  • total(10, 25, 15)15 is an int, so the exact match (int, int, int) wins over widening to double.
  • total(40, 60, 0.10)0.10 is a double, so only the discount version applies. It returns a double: 90.0.
8 Line by line

One overload calling another

The two-argument total delegates to the three-argument one. Watch two frames with the same method name sit on the stack at the same time.

Worked examples: real problems, solved and traced in memory

Three everyday problems where overloading keeps the code short. In each one, the simple versions delegate to a full version, so the real rule is written once. Run each solution, then step through it.

Example 1Mobility · ride fare

Fare with and without surge

Problem statement

A ride app charges Rs 12 per km. At peak time it multiplies the fare by a surge factor. Write fare(int km) and fare(int km, double surge), where the first simply calls the second with a surge of 1.0. Print the fare for a 10 km ride at normal time and at 1.5× surge.

Input
10 km normal, 10 km at 1.5x surge
Expected output
Normal: Rs 120.0
Peak: Rs 180.0

Approach

  1. Write the full rule once in fare(int km, double surge): km * 12 * surge.
  2. fare(int km) returns fare(km, 1.0).
  3. javac picks the version from the number of arguments in each call.

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

Example 2Construction · estimates

Area of a room, a plot and a circular tank

Problem statement

A builder's estimator needs areas: a square room (one side), a rectangular plot (length and width) and a circular water tank (radius, a double). Write three overloads named area and print each result.

Input
square side 4, plot 12 x 9, tank radius 1.5
Expected output
Room: 16 sq ft
Plot: 108 sq ft
Tank: 7.0649999999999995 sq m

Approach

  1. area(int side) calls area(side, side): a square is a rectangle.
  2. area(int l, int w) returns l * w.
  3. area(double r) uses 3.14 × r × r. The double argument selects it.

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

Example 3Retail · invoices

Invoice lines for one item or many

Problem statement

A shop prints invoice lines. line(String item, int price) is for a single item; line(String item, int price, int qty) is for several. Both must print the same format, so the single-item version calls the other with qty 1.

Input
Pen Rs 10 (1 piece), Notebook Rs 45 x 3
Expected output
Pen x1 = Rs 10
Notebook x3 = Rs 135

Approach

  1. Write the formatting once in the three-parameter version.
  2. The two-parameter version passes qty = 1.
  3. main calls each version once.

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

9 Quiz and revision

Check yourself

Quick revision

Lesson 5.4 in one screen

  • Overloading = same method name, different parameter lists (number, type or order of types) in one class.
  • Signature = name + parameter types. Return type, parameter names, static and access modifiers don't count.
  • javac chooses at compile time: phase 1 exact/widening → phase 2 boxing → phase 3 varargs; then the most specific method.
  • No single most specific method → reference to … is ambiguous. Same signature twice → already defined.
  • The choice is stored in the bytecode as a descriptor like area:(I)I.
  • Good overloads all do the same job; short versions delegate to the full one.
  • Constructors and main can be overloaded too.

Interview and viva questions

What is method overloading? Give an example from the JDK.

Defining several methods with the same name but different parameter lists in one class. The compiler picks one based on the argument types. System.out.println is the classic example: there are versions for int, long, double, char, boolean, char[], String and Object.

Why can't we overload by return type alone?

Because a call doesn't always use its result (calc(5); is a valid statement), the compiler would have no way to choose. Java therefore defines the signature as name plus parameter types only, and two methods with the same signature are a compile error.

What is the difference between overloading and overriding?

Overloading: same name, different parameters, usually in the same class, chosen by the compiler (static polymorphism). Overriding: a subclass gives a new body for a method with the same signature as in the parent, chosen at run time from the object's actual class (dynamic polymorphism).

In what order does Java try widening, boxing and varargs?

First only exact matches and widening, then boxing/unboxing, and varargs only as the last resort. Within a phase, the most specific method wins. This order keeps old code (written before boxing and varargs existed in Java 5) behaving the same way.

Can static methods be overloaded? Can main be overloaded?

Yes to both. Overloading works for static and instance methods alike. You can write main(int x), but the JVM only starts a program from public static void main(String[] args).

Practice problems

Write overloads, predict which one javac chooses, and fix the classic mistakes. The last three are real business tasks.

P1Easy

max of two or three

Write max(int a, int b) and max(int a, int b, int c). The three-argument version must reuse the two-argument one.

Input
max(4, 9), max(7, 2, 5)
Expected output
9
7
Show solution and explanation
public class Main {
    static int max(int a, int b) {
        return a > b ? a : b;
    }

    static int max(int a, int b, int c) {
        return max(max(a, b), c);
    }

    public static void main(String[] args) {
        System.out.println(max(4, 9));
        System.out.println(max(7, 2, 5));
    }
}
9 7

How it works: the calls differ in the number of arguments, so javac picks each version from the count alone. Reusing max(a, b) keeps the comparison logic in one place.

P2Easy

Describe any value

Write three overloads of describe for an int, a double and a String, each printing the type and the value.

Input
describe(42), describe(3.5), describe("UPI")
Expected output
int: 42
double: 3.5
String: UPI (3 chars)
Show solution and explanation
public class Main {
    static void describe(int x)    { System.out.println("int: " + x); }
    static void describe(double x) { System.out.println("double: " + x); }
    static void describe(String x) { System.out.println("String: " + x + " (" + x.length() + " chars)"); }

    public static void main(String[] args) {
        describe(42);
        describe(3.5);
        describe("UPI");
    }
}
int: 42 double: 3.5 String: UPI (3 chars)

How it works: overloading by parameter type. This is exactly how println handles different types.

P3Easy

Predict the choice

Given show(int) and show(double), predict what each call prints: a byte, a char, a long and a float.

Input
show((byte) 3), show('C'), show(10L), show(2.5f)
Expected output
3 -> int
67 -> int
10.0 -> double
2.5 -> double
Show solution and explanation
public class Main {
    static void show(int x)    { System.out.println(x + " -> int"); }
    static void show(double x) { System.out.println(x + " -> double"); }

    public static void main(String[] args) {
        show((byte) 3);
        show('C');
        show(10L);
        show(2.5f);
    }
}
3 -> int 67 -> int 10.0 -> double 2.5 -> double

How it works: byte and char widen to int, and the int version is more specific than double. A long can't narrow to int, and a float can't either, but both widen to double. Note that 'C' prints as 67 inside the int version.

P4Medium

Widening, boxing or varargs?

Three overloads exist: pick(long), pick(Integer) and pick(int...). Show which one a call with an int uses, then what happens with an Integer object and with two ints.

Input
pick(5), pick(Integer.valueOf(5)), pick(5, 6)
Expected output
long
Integer
varargs (2 values)
Show solution and explanation
public class Main {
    static void pick(long x)    { System.out.println("long"); }
    static void pick(Integer x) { System.out.println("Integer"); }
    static void pick(int... x)  { System.out.println("varargs (" + x.length + " values)"); }

    public static void main(String[] args) {
        pick(5);
        pick(Integer.valueOf(5));
        pick(5, 6);
    }
}
long Integer varargs (2 values)

How it works: pick(5): phase 1 (widening) already finds pick(long). pick(Integer.valueOf(5)): an Integer matches pick(Integer) exactly in phase 1. pick(5, 6): nothing takes two parameters, so only phase 3 (varargs) works.

P5Medium

Fix: overload by return type

This code doesn't compile: int convert(int x) and double convert(int x). Rewrite it so one method converts paise to rupees (returns double) and the other doubles an amount, with names or parameters that make the two clearly different.

Input
250 paise, Rs 40 doubled
Expected output
2.5
80
Show solution and explanation
public class Main {
    static double toRupees(int paise) {
        return paise / 100.0;
    }

    static int doubled(int amount) {
        return amount * 2;
    }

    public static void main(String[] args) {
        System.out.println(toRupees(250));
        System.out.println(doubled(40));
    }
}
2.5 80

How it works: two methods with signature convert(int) can't exist together, whatever their return types. Because they do different jobs, the right fix is two descriptive names, not an artificial parameter change.

P6Business problem

Shipping cost with optional express

An online store charges Rs 40 plus Rs 10 per kg. Express delivery adds Rs 60. Write shipping(int kg) and shipping(int kg, boolean express); the first calls the second with false.

Input
2 kg normal, 2 kg express, 5 kg normal
Expected output
2 kg: Rs 60
2 kg express: Rs 120
5 kg: Rs 90
Show solution and explanation
public class Main {
    static int shipping(int kg) {
        return shipping(kg, false);
    }

    static int shipping(int kg, boolean express) {
        int cost = 40 + 10 * kg;
        if (express) {
            cost += 60;
        }
        return cost;
    }

    public static void main(String[] args) {
        System.out.println("2 kg: Rs " + shipping(2));
        System.out.println("2 kg express: Rs " + shipping(2, true));
        System.out.println("5 kg: Rs " + shipping(5));
    }
}
2 kg: Rs 60 2 kg express: Rs 120 5 kg: Rs 90

How it works: the optional detail (express) has a sensible default. Most callers use the short version; the pricing rule exists only once.

P7Business problem

Masking a phone number or an account number

A bank's SMS shows only the last 4 digits. Write mask(long phone) and mask(String accountNo); the long version converts to a String and calls the String version.

Input
phone 9876543210, account "001234567890"
Expected output
XXXXXX3210
XXXXXXXX7890
Show solution and explanation
public class Main {
    static String mask(String accountNo) {
        int keep = 4;
        String last = accountNo.substring(accountNo.length() - keep);
        return "X".repeat(accountNo.length() - keep) + last;
    }

    static String mask(long phone) {
        return mask(String.valueOf(phone));
    }

    public static void main(String[] args) {
        System.out.println(mask(9876543210L));
        System.out.println(mask("001234567890"));
    }
}
XXXXXX3210 XXXXXXXX7890

How it works: the phone number needs L because 9876543210 is too big for an int. The long overload converts once and reuses the masking logic. (String.repeat needs Java 11+.)

P8Business problem

Simple interest with a default rate

A loan calculator uses 9% per year unless a rate is given. Write interest(int principal, int years) and interest(int principal, int years, double rate), and print both results for Rs 50,000 over 2 years (default rate and 7.5%).

Input
50000 for 2 years; at 9% and at 7.5%
Expected output
Default 9%: Rs 9000.0
At 7.5%:   Rs 7500.0
Show solution and explanation
public class Main {
    static final double DEFAULT_RATE = 9.0;

    static double interest(int principal, int years) {
        return interest(principal, years, DEFAULT_RATE);
    }

    static double interest(int principal, int years, double rate) {
        return principal * rate * years / 100;
    }

    public static void main(String[] args) {
        System.out.println("Default 9%: Rs " + interest(50000, 2));
        System.out.println("At 7.5%:   Rs " + interest(50000, 2, 7.5));
    }
}
Default 9%: Rs 9000.0 At 7.5%: Rs 7500.0

How it works: simple interest = P × R × T / 100. The default rate lives in a named constant, and the two-argument version passes it on, so changing the default needs one edit.

Real-world scenario questions (asked in company interviews)

Interviewers like overloading because it tests whether you know what the compiler does, not just the syntax. Think of your answer first, then open the model answer.

Payments · API designFresher

“Can I add a version that returns a receipt?”

A PaymentService has void pay(int amount, String upiId). A teammate wants a version that returns a Receipt and writes Receipt pay(int amount, String upiId) next to it. It won't compile. Explain and suggest a fix.

The interviewer is checking: do you know that the return type is not part of the signature?

Show a strong answer
  • Both methods have the signature pay(int, String). Return type doesn't count, so javac reports method pay(int,String) is already defined.
  • Either change the existing method to return the Receipt (callers can ignore it), or give the new one a clear name like payAndGetReceipt.
  • Don't “fix” it by swapping parameter order; pay(String, int) compiles but invites mistakes.

Avoid: saying the JVM will choose based on how the result is used.

E-commerce · bug hunt1–3 years

The discount that became an extra item

A cart has total(int a, int b, int extraItem) and total(int a, int b, double discount). A developer meant to give 10% off and wrote total(400, 600, 10). Customers were charged Rs 1010 instead of Rs 900. Why, and how would you prevent it?

The interviewer is checking: do you understand overload resolution and API design risks?

Show a strong answer
  • 10 is an int, so the exact match total(int, int, int) is chosen at compile time: 400 + 600 + 10 = 1010.
  • The overloads do different jobs under one name. Rename them: totalWithExtra and totalWithDiscount, or use a Discount type so the compiler catches mistakes.
  • Add a unit test for the discount path; this bug passes compilation silently.

Avoid: overloads whose meaning changes with the parameter type.

Library · compatibility1–3 years

Adding an overload broke someone's build

Your shared library had send(String userId, Object payload). You added send(String userId, String message). Another team's code send(id, null) still compiles, but now behaves differently. What happened?

The interviewer is checking: do you know that overload choice is fixed at compile time and depends on “most specific”?

Show a strong answer
  • When they recompiled, null fits both, and String is more specific than Object, so javac now picks the new method.
  • Code compiled against the old version keeps calling the old method until it is recompiled, because the descriptor is stored in their class files.
  • Adding overloads to a public API is a compatibility risk; document it, or use a new method name.

Avoid: assuming that adding a method can never change existing behaviour.

Analytics · performance3+ years

Why does Math.max have four versions?

A junior asks why the JDK has max(int,int), max(long,long), max(float,float) and max(double,double) instead of one max(Number, Number). What do you say?

The interviewer is checking: can you connect overloading to boxing costs and type safety?

Show a strong answer
  • Primitive overloads avoid boxing: no Integer objects are created in hot loops, and the result keeps its exact type.
  • Each version uses the right comparison for its type (for doubles, special handling of NaN and −0.0).
  • Callers still write just Math.max(a, b); the compiler picks the fastest matching version.

Avoid: “it's just old code”. It is a deliberate performance and correctness choice.