Skip to Content
Course content

29: Method Overriding vs Overloading

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I want to show you a snippet of code that looks perfectly fine at first glance. I once reviewed a PR from a junior dev who spent three hours wondering why their "specialized" logic wasn't firing. They were convinced the JVM was broken. It wasn't; they had just fallen into the classic trap of confusing overriding with overloading.

class PaymentProcessor {
    void processPayment(double amount) {
        System.out.println("Processing generic payment of $" + amount);
    }
}

class CreditCardProcessor extends PaymentProcessor {
    // The dev intended to override the base method here
    void processPayment(int amount) {
        System.out.println("Processing credit card payment of $" + amount + " with 2% fee");
    }
}

public class Main {
    public static void main(String[] args) {
        PaymentProcessor processor = new CreditCardProcessor();
        processor.processPayment(100.0); 
    }
}

If you run this, you'll see "Processing generic payment of $100.0". The developer expected the credit card logic to run, but Java ignored the method in CreditCardProcessor entirely. Why?

The Signature Mismatch Trap

Here is the problem: the developer didn't override the method; they overloaded it. Overriding requires the method signature (name and parameter types) to be identical. In the base class, we have a double. In the subclass, the dev used an int.

To Java, processPayment(double) and processPayment(int) are two completely different methods that just happen to share a name. Because the variable processor is declared as a PaymentProcessor, Java looks for a method that takes a double. It finds one in the base class and uses it. The int version in the subclass just sits there, unused and invisible, because it's treated as a brand new method specific to credit cards.

Forcing the Compiler to Help You

The fix is simple, but the habit is what matters. You should almost always use the @Override annotation. If the dev had written @Override above the method in CreditCardProcessor, the code wouldn't have even compiled. The compiler would have screamed: "Method does not override a method from its superclass."

class CreditCardProcessor extends PaymentProcessor {
    @Override 
    void processPayment(double amount) { // Changed int to double
        System.out.println("Processing credit card payment of $" + amount + " with 2% fee");
    }
}

Now, when you call processor.processPayment(100.0), Java sees that the CreditCardProcessor has provided a specific implementation for that exact signature and uses it. This is polymorphism in action.

Knowing Which Tool to Reach For

I often see people use these interchangeably in conversation, but in your code, they serve opposite purposes. Think of it this way:

  • Overloading is about flexibility. You use it when you want the same action to work with different types of input. For example, a Logger class might have log(String message) and log(Exception e). Same name, different data, same class.
  • Overriding is about specialization. You use it when a subclass needs to do something differently than its parent. A Dog class overrides the makeSound() method of the Animal class to bark instead of making a generic noise.

Just remember: Overloading happens at compile-time (the compiler decides which version to call based on the arguments). Overriding happens at runtime (Java looks at the actual object type on the heap to decide which version to call).




📋 Practical Task

Implementing a Tiered Discount System

You are building a retail system where different customer types get different discount calculations. Your task is to implement a system that uses both overriding and overloading correctly.

Requirements:

  • Create a base class Customer with a method calculateDiscount(double price) that returns a 5% discount (price * 0.05).
  • Create a subclass VIPCustomer that overrides calculateDiscount(double price) to provide a 20% discount.
  • In the VIPCustomer class, overload the calculateDiscount method by creating a version that accepts (double price, int loyaltyYears). If loyaltyYears is greater than 5, the discount should be 30%; otherwise, it stays at 20%.
  • In your main method, instantiate a VIPCustomer but store it in a Customer reference. Call the standard discount method to verify overriding works. Then, cast it to a VIPCustomer to call the overloaded loyalty method.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.