Exception Handling with Method Overriding
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
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());
}
}
}
}load failed: marks.csv missing
cached marks for NavikaHow it works
- The parent's
load()declaresIOException, somaincatchesIOException. FileLoadernarrows it toFileNotFoundException, a subclass. The caller'scatch (IOException e)still covers it.MemoryLoaderdeclares nothing; it can't fail with I/O at all. Also allowed.- The same
mainworks safely with both overrides.
What the Compiler Rejects
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 { }
}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 errorsException is broader than IOException, and Plain.run() declared nothing at all, so adding IOException there is a new checked exception.
| Parent declares | Override declares | Allowed? |
|---|---|---|
throws IOException | throws IOException | Yes |
throws IOException | throws FileNotFoundException | Yes (narrower) |
throws IOException | nothing | Yes |
throws IOException | throws Exception | No (broader) |
| nothing | throws IOException | No (new checked) |
| anything | throws IllegalStateException | Yes (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:
CtorEx.java:3: error: unreported exception IOException; must be caught or declared to be thrown
class Child extends Base { Child() { } }
^
1 errorHere 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
- 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. - Harder: Write an interface
Exporterwithvoid export(String data) throws IOException, and two implementations: one that writes nothing and declares no exceptions, and one that throwsFileNotFoundException.
Key Takeaways
Was this tutorial helpful?