※本記事は、Simon Roberts、Mikalai Zaikinによる”Quiz Yourself: Custom Exceptions (Advanced)“を翻訳したものです。
例外の宣言が必要になる正確な条件
著者:Simon Roberts、Mikalai Zaikin
2019年10月4日
その他の設問はこちらから
過去にこのクイズの設問に挑戦したことがある方なら、どれ1つとして簡単な問題はないことをご存じでしょう。クイズの設問は、認定試験の中でも難しいものに合わせています。「中級者向け」「上級者向け」というレベルは、設問の難易度ではなく、対応する試験による分類です。しかし、ほとんどすべての場合において、「上級者向け」の方が難しくなります。設問は認定試験対策として作成しており、認定試験と同じルールの適用を意図しています。文章は文字どおりに解釈してください。回答者を引っかけようとする設問ではなく、率直に言語の詳細な知識を試すものだと考えてください。
この設問では、カスタム例外を作成したいと思います。 次のコードについて:
class UnknownException extends Error {}
class BusinessException1 extends Exception {}
class BusinessException2 extends Exception {}
class Application {
public Object doId() /* point n1 */ {
// line n2
}
}
次に示す一連の追加を行った場合(それぞれの追加は独立して行うものとします)、正常にコンパイルできるのはどれですか。3つ選んでください。
- point n1に:
throws BusinessException2
line n2に:return "Hi"; - point n1に:何も追加しない
line n2に:throw new UnknownException() - point n1に:
throws Exception
line n2に:throw new BusinessException1() - point n1に:
throws BusinessException1
line n2に:throw new BusinessException2() - point n1に:
throws BusinessException1
line n2に:throw new Exception()
解答:Javaでは、例外をチェック例外および非チェック例外という2つの大きなカテゴリに分類しています。ただし、JVMでは異なります(後で補足として説明します)。非チェックのカテゴリはさらに大きく2つに分けることができますが、設問とは無関係であることから、その点についてここでは触れません。
説明に補足を入れるのは少し早いですが、これは厄介な問題であるため、早く片付けてしまいます。「JVMでは異なります」というただし書きはどういう意味でしょうか。最初に言っておきますが、気にしないのであれば知る必要はありません。ただし、背景知識としては興味深いものかもしれません。Javaプログラムでは、チェック例外をスローする可能性があるコードを含むメソッドは、その事実を宣言しなければなりません。また、Java言語のルールのほとんど(アクセス可能性や代入の互換性など)は、クラス・ローダーがバイトコードに強制しています。しかし、チェック例外のルールは違います。バイトコードから見れば、すべての例外は等しいもので、どの例外でもいつでも宣言なしにスローすることができます。
java.lang.Throwableクラスは、スロー可能なもののベース・クラスで、java.lang.Errorおよびjava.lang.Exceptionという2つのサブクラスがあります。Exceptionには、java.lang.RuntimeExceptionというサブクラスがあります。Javaでは、ErrorでもRuntimeExceptionでもないすべてのThrowableはチェック例外です。 つまり、この2つのクラスとそのサブクラスは非チェック例外で、それ以外のすべてはチェック例外です。以上を踏まえると、UnknownExceptionはErrorのサブクラスであることから非チェック例外であり、BusinessException1とBusinessException2はいずれもチェック例外であることがわかります。
ところで、Javaでは、非チェック例外をスローすることについて、何の制約も課していません。つまり、非チェック例外は、メソッドがその非チェック例外をスローする可能性があるかないかにかかわらず、また、非チェック例外のスローが可能であるかないかにかかわらず、宣言しても宣言しなくても構いません。
対照的に、メソッドがコール元にチェック例外をスローできるのは、throws句でその可能性を宣言している場合に限られます。メソッドのthrows句では、スローされる可能性がある実際の例外の親を宣言するだけで十分です。子の例外は親の例外型のインスタンスであるからです。
さらに、特定の例外を列挙するthrows句は、単にそのメソッドがその例外をスローできるようにしているだけで、必ずそのメソッドがその例外をスローしなければならないということではありません。ほとんどの例外は特別な状況でスローされることから、例外が発生しない可能性もあることは明らかです。しかし、メソッドでその例外がスローされる可能性がまったくなくても構いません。例外の宣言は、将来的な変更のためや、オーバーライドするメソッドがその例外をスローできるようにするために行うことも可能です。
それでは、選択肢を確認していきます。
選択肢Aでは、チェック例外を指定したthrows句を宣言していますが、メソッドの本体でその例外がスローされる可能性はありません。先ほど触れたように、これは問題ないため、選択肢Aは正解です。
選択肢Bでは、UnknownExceptionをスローしています。これは実際にはErrorのサブクラスです(あまりよい名前ではありません)。しかし、ここでさらに重要なのは、この例外が非チェック例外であることです。 非チェック例外であるため、throws句で宣言されていてもいなくても問題ありません。つまり、選択肢Bも正解です。
選択肢Cでは、特定のチェック例外BusinessException1をスローしていますが、Exceptionをスローすると宣言しています。前述のように、BusinessException1はExceptionである(is-a関係)ため、これは問題ありません。なお、選択肢AおよびBとは異なり、この場合はthrows句を省略することはできません。したがって、選択肢Cも正解です。
選択肢Dではチェック例外BusinessException2をスローしていますが、BusinessException1をスローすると宣言しています。このthrows句は問題ありませんが、兄弟関係にあたる例外をスローできるようにするためには、これでは不十分です。十分簡単なことですが、ここで問題になるのは、2つのBusinessException型に、似たような名前と共通の親を持つということ以外には何の関係もないことです。そのため、この場合、メソッドはthrows句で宣言されていないチェック例外をスローすることになり、選択肢Dは誤りです。
選択肢Eも、選択肢Dと本質的に同じ理由で失敗します。すべてのBusinessException1はExceptionです(is-a関係)が、継承関係は一方向であるため、ExceptionはBusinessException1ではありません。したがって、スローされる可能性がある例外に対してthrows句が不十分であるため、選択肢Eは誤りです。
正解は選択肢A、B、Cです。
Java Magazine 日本版Vol.47の他の記事
Java 13のswitch式と再実装されたSocket APIの内側
Javaにテキスト・ブロックが登場
言語の内側:シールド型
TeaVMを使ってブラウザでJavaを動かす
ツールをよく知る
クイズに挑戦:1次元配列(中級者向け)
クイズに挑戦:ロケールの読取りと設定(上級者向け)
クイズに挑戦:関数型インタフェース(上級者向け)
![]() |
Simon RobertsSimon Roberts:Sun Microsystemsがイギリスで初めてJavaの研修を行う少し前にSunに入社し、Sun認定Javaプログラマー試験とSun認定Java開発者試験の作成に携わる。複数のJava認定ガイドを執筆し、現在はフリーランスでPearson InformITにおいて録画やライブによるビデオ・トレーニングを行っている(直接またはO’Reilly Safari Books Onlineサービス経由で視聴可能)。OracleのJava認定プロジェクトにも継続的に関わっている。 |
![]() |
Mikalai ZaikinMikalai Zaikin:ベラルーシのミンスクを拠点とするIBA IT ParkのリードJava開発者。OracleによるJava認定試験の作成に携わるとともに、複数のJava認定教科書のテクニカル・レビューを行っている。Kathy Sierra氏とBert Bates氏による有名な学習ガイド『Sun Certified Programmer for Java』では、3版にわたってテクニカル・レビューを務めた。 |


