Exception Handling with Method Overriding

Naveen AnamaneniUpdated · 5 min read

Code often calls a method through a parent type: Loader loader = new FileLoader(); loader.load();. That code was written against the parent's throws clause, so it's only prepared for the exceptions the parent declares. If a child's override could throw a new or broader checked exception, those callers would suddenly face a problem they never handled. Java prevents that with one rule: an override may throw fewer or narrower checked exceptions, never more or broader.

What an Override May Declare

Same, narrower or none: allowed. Broader or new checked: rejected
Loaders.java▶ Try it
import java.io.FileNotFoundException;
import java.io.IOException;
 
public class Loaders {
    static class Loader {
        String load() throws IOException { return "data"; }
    }
 
    static class FileLoader extends Loader {
        @Override
        String load() throws FileNotFoundException {
            throw new FileNotFoundException("marks.csv missing");
        }
    }
 
    static class MemoryLoader extends Loader {
        @Override
        String load() { return "cached marks for Navika"; }
    }
 
    public static void main(String[] args) {
        Loader[] loaders = {new FileLoader(), new MemoryLoader()};
        for (Loader l : loaders) {
            try {
                System.out.println(l.load());
            } catch (IOException e) {
                System.out.println("load failed: " + e.getMessage());
            }
        }
    }
}
Output
load failed: marks.csv missing
cached marks for Navika

How it works

  1. The parent's load() declares IOException, so main catches IOException.
  2. FileLoader narrows it to FileNotFoundException, a subclass. The caller's catch (IOException e) still covers it.
  3. MemoryLoader declares nothing; it can't fail with I/O at all. Also allowed.
  4. The same main works safely with both overrides.

What the Compiler Rejects

OvEx.java
import java.io.*;
class Loader { String load() throws IOException { return "data"; } }
class BadLoader extends Loader {
    @Override String load() throws Exception { return "bad"; }
}
class Plain { void run() { } }
class BadPlain extends Plain {
    @Override void run() throws IOException { }
}
Compiler output
OvEx.java:4: error: load() in BadLoader cannot override load() in Loader
    @Override String load() throws Exception { return "bad"; }
                     ^
  overridden method does not throw Exception
OvEx.java:8: error: run() in BadPlain cannot override run() in Plain
    @Override void run() throws IOException { }
                   ^
  overridden method does not throw IOException
2 errors

Exception is broader than IOException, and Plain.run() declared nothing at all, so adding IOException there is a new checked exception.

Parent declaresOverride declaresAllowed?
throws IOExceptionthrows IOExceptionYes
throws IOExceptionthrows FileNotFoundExceptionYes (narrower)
throws IOExceptionnothingYes
throws IOExceptionthrows ExceptionNo (broader)
nothingthrows IOExceptionNo (new checked)
anythingthrows IllegalStateExceptionYes (unchecked)

Constructors Work the Other Way

Constructors aren't overridden, but a subclass constructor always calls its parent's constructor. So it must handle, or declare, whatever the parent constructor declares:

Compiler output
CtorEx.java:3: error: unreported exception IOException; must be caught or declared to be thrown
class Child extends Base { Child() { } }
                                   ^
1 error

Here Base() declares throws IOException, so Child() must declare it too (or a broader type). Because the super() call has to come first, it can't wrap that call in a try.

Common Mistakes

Adding a checked exception to an override "just in case". It's a compile error; handle it inside, or wrap it in an unchecked one.

Forgetting that interface methods count as parents too. Implementations follow the same rule.

Expecting the rule to cover unchecked exceptions. They're never restricted.

Interview Corner

What are the rules for exceptions when overriding a method?

The overriding method can declare the same checked exceptions as the overridden method, subclasses of them, or none at all. It can't declare new checked exceptions or broader ones, because callers using the parent type are only prepared for the parent's declared exceptions. Unchecked exceptions can be thrown or declared freely.

Can a subclass constructor throw fewer exceptions than the parent constructor?

No. The subclass constructor must call a parent constructor, and since that call comes first and can't be wrapped in a try, the subclass constructor must declare every checked exception the parent constructor declares, or a supertype of it.

Practice

  1. Easy: For a parent void save() throws SQLException, say which overrides compile: void save(), void save() throws Exception, void save() throws SQLException, IOException, void save() throws IllegalArgumentException.
  2. Harder: Write an interface Exporter with void export(String data) throws IOException, and two implementations: one that writes nothing and declares no exceptions, and one that throws FileNotFoundException.

Quick check: Exceptions and overriding

4 questions
1The parent declares throws IOException. Which override is allowed?
2The parent declares nothing. Can the override declare throws IOException?
3Can an override throw IllegalStateException when the parent declares nothing?
4The parent constructor declares IOException. What must the child constructor do?

Key Takeaways

Was this tutorial helpful?

Lesson 232 of 482