Javaのオーバーライドにおけるthrows:例外の親子階層はなぜ重要なのか?
オーバーライド時の例外ルールとは
Javaでチェック例外(検査例外)をスローする可能性のあるメソッドを呼び出す場合、その例外クラス自身、またはそのスーパークラスでcatchする必要があります。これに違反するとコンパイルエラーになります。
同じ考え方は、メソッドのオーバーライドにも当てはまります。スーパークラスのメソッドをオーバーライドするとき、サブクラス側のメソッドがスローできる例外には以下の制約があります。
- サブクラスのメソッドは、親メソッドと同じ例外、またはそのサブタイプ(より狭い例外)をスローできる。
- 親メソッドがスローする例外のスーパータイプ(より広い例外)をスローしてはならない。
- 例外を一切スローしない形でオーバーライドすることも可能。
つまり、オーバーライドにおいても例外の親子階層(継承関係)が重要になります。以下、具体的なコード例で確認していきましょう。
正しい例:同じ例外またはサブタイプをスロー
ここでは、Demo → SuperTest → Test という継承階層を持つ3つのクラスを例にします。DemoクラスとSuperTestクラスは、どちらもsample()メソッドを持っています。
import java.io.IOException;
import java.io.EOFException;
class Demo {
public void sample() throws IOException {
System.out.println("Demoクラスのsample()メソッド");
}
}
class SuperTest extends Demo {
// 親がスローするIOExceptionのサブタイプをスロー → OK
public void sample() throws EOFException {
System.out.println("SuperTestクラスのsample()メソッド");
}
}
public class Test extends SuperTest {
public static void main(String args[]) {
Demo obj = new SuperTest();
try {
obj.sample();
} catch (EOFException ex) {
System.out.println("例外をキャッチしました");
}
}
}
出力
SuperTestクラスのsample()メソッド
この例では、親クラスDemoのsample()がIOExceptionをスローし、子クラスSuperTestのsample()はそのサブタイプであるEOFExceptionをスローしています。これは「同じ例外か、より狭い例外」というルールに合致しているため、コンパイルは正常に成功します。
誤った例:スーパータイプをスローするとコンパイルエラー
逆に、親メソッドがスローする例外よりも上位(広い範囲)の例外を子クラスでスローしようとすると、コンパイルエラーとなります。
import java.io.IOException;
import java.io.EOFException;
class Demo {
public void sample() throws EOFException {
System.out.println("Demoクラスのsample()メソッド");
}
}
class SuperTest extends Demo {
// 親がスローするEOFExceptionより広いIOExceptionをスロー → コンパイルエラー
public void sample() throws IOException {
System.out.println("SuperTestクラスのsample()メソッド");
}
}
出力(コンパイルエラー)
Test.java:12: エラー: SuperTestのsample()はDemoのsample()をオーバーライドできません
public void sample() throws IOException {
^
オーバーライドされたメソッドはIOExceptionをスローしません
エラー1個
なぜこのようなルールがあるのか
この制約の背景にあるのは、ポリモーフィズム(多態性)による置換可能性の維持です。親クラス型の変数経由でメソッドを呼び出すコードは、「親が宣言した例外」だけを前提としてcatch処理を記述します。もし子クラスがより広い例外を自由にスローできてしまうと、呼び出し側が捕捉できない例外が発生する恐れがあり、親子間のメソッド契約が崩れてしまうからです。
同様に、catch側でも「発生した例外と同じクラス、またはそのスーパークラス」で捕捉しなければならないのは、同じ理由によります。例外の階層構造を正しく理解しておくことは、堅牢なJavaプログラムを書くうえで欠かせない知識だといえます。
補足:非チェック例外の場合
なお、上記のルールが厳密に適用されるのは、IOExceptionやSQLExceptionなどのチェック例外です。RuntimeExceptionやErrorといった非チェック例外については、オーバーライド時にどのようなthrows宣言を行ってもコンパイルエラーにはなりません。ただし、設計の一貫性を保つ観点からは、親クラスの宣言に合わせておくことが推奨されます。
-
Javaのfinalキーワードとは?変数・メソッド・クラスでの使い方を解説
finalは、Javaにおける非アクセス修飾子のひとつです。クラス、メソッド、変数の実装を「確定」させるために使用され、これらが後から変更されることを防ぐ役割を持ちます。本記事では、finalキーワードの基本的な概念と、変数・メソッド・クラスそれぞれでの使い方をコード例とともに解説します。final変数final変数は、明示的に初期化できるのは一度だけです。また、finalで宣言された参照変数は、別のオブジェクトを参照するように再代入することはできません。ただし、オブジェクト内部のデータ自体は変更可能です。つまり、オブジェクトの「状態」は変えられるものの、「参照先」は変えられないという点に注意
-
Pythonにおけるメソッドのオーバーライドとは?基本と実例をわかりやすく解説
Pythonでは、親クラス(スーパークラス)で定義されたメソッドを、子クラス(サブクラス)側で自由に上書きすることができます。この仕組みを「メソッドのオーバーライド」と呼びます。オーバーライドを行う主な理由は、サブクラスにおいて親クラスとは異なる特別な処理や振る舞いを持たせたい場合です。継承した機能をそのまま使うのではなく、自分のクラスに合わせてカスタマイズできるのが大きなメリットです。メソッドのオーバーライドの例以下は、親クラスのメソッドを子クラスで上書きする基本的なサンプルコードです。#!/usr/bin/python3 class Parent: # 親クラスを定義 def