Skip to content

JAVA FOUNDATIONS · JAVA 17

Java inheritance and polymorphism: dispatch the right behaviour

Use a base type to process different notification objects and see which overridden method executes.

Forsk Coding School learning resources · Updated 2026-10-26

What you will learn

  • Recognize a genuine is-a relationship
  • Explain runtime dispatch of an instance method
  • Use @Override to catch a mistaken method signature

Before you start: Classes, constructors and instance methods.

Let the caller depend on a shared operation

Suppose a small application prepares different notification messages. The caller needs a message from every notification, but should not contain a growing list of type checks. A base class provides the message operation. An EmailNotice specializes that operation by overriding the instance method. A loop can then call message through a Notice reference.

This example only returns text. It does not send an email, contact a server or require credentials. Keeping side effects out makes the dispatch mechanism easy to test. You can change the array contents and inspect the resulting strings without involving a network delivery system.

Separate the reference type from the object type

In Notice item = new EmailNotice(), the variable's declared type is Notice and the actual object is an EmailNotice. The declared type controls which members the compiler lets the caller access. For an overridden instance method, the runtime object determines the implementation invoked. That is why the second item prints an email-specific message.

@Override asks the compiler to check that the method really overrides an inherited method. A misspelled method name or a different parameter list could otherwise create a new method instead. Constructors are not overridden. Static methods also do not use this same instance-method dispatch mechanism; keep that distinction explicit in interview answers.

Use inheritance when the relationship fits

An EmailNotice is a Notice, so the example has a plausible substitution relationship. Do not use inheritance merely to borrow a few unrelated methods. If a class has another object as a collaborator, composition is often a clearer description: a Report might have a Formatter rather than be a Formatter. The next lesson demonstrates that contract through an interface.

To test polymorphism, call the shared operation through the base type and assert the results for each concrete object. Calling only email.message() on an EmailNotice variable does not exercise the caller's use of the shared base abstraction. A useful test reflects the way the application actually invokes the behaviour.

Run the worked example

Save as Main.java. Run javac Main.java, then java Main.

Main.java
class Notice {
    String message() { return "General notice"; }
}
class EmailNotice extends Notice {
    @Override
    String message() { return "Email notice"; }
}
public class Main {
    public static void main(String[] args) {
        Notice[] notices = {new Notice(), new EmailNotice()};
        for (Notice item : notices) {
            System.out.println(item.message());
        }
    }
}

Expected result

General notice
Email notice
  1. The array stores references compatible with Notice.
  2. The first object uses the base implementation.
  3. The second object dispatches to EmailNotice.message even though the loop variable is Notice.

Common mistakes to check

  • Changing a parameter list overloads a method instead of overriding the original signature.
  • Do not infer instance-method behaviour from a static method with the same name.
  • Prefer @Override so the compiler verifies the intended relationship.

Try it yourself

Create a Transport class whose label method returns Transport and a Bus subclass returning Bus. Put both objects in a Transport array and print labels through the base type.

Give me a hint

Override the same no-argument instance method and iterate using Transport variables.

Compare with one working solution
Main.java
class Transport {
    String label() { return "Transport"; }
}
class Bus extends Transport {
    @Override String label() { return "Bus"; }
}
public class Main {
    public static void main(String[] args) {
        for (Transport item : new Transport[]{new Transport(), new Bus()}) {
            System.out.println(item.label());
        }
    }
}

Expected result

Transport
Bus

Quick check

Choose an answer, then check the explanation. Your answer stays in this page and is not saved as a student record.

Which implementation runs for Notice n = new EmailNotice(); n.message();?

Keep learning

Explain the result in your own words, change one input and predict the new output. For additional structured assignments, explore the workbook sample before requesting the full pack.

Explore the practice workbook

Reference documentation

Use the official reference to check language details. The explanations, examples and exercises on this page are written for this learning resource.