1. はじめに
プログラミングでは、メソッドがエラーに遭遇しても、適切な対処方法がわからないことがよくあります。たとえばメソッドがファイルを読み込むとして、ファイルが存在しない場合にどうするか(ユーザーに確認する? プログラムを終了する? 別のファイルを試す?)はメソッド自身には決められません。こうした場合、メソッドは自分で処理せず、呼び出し元に「責任を渡す」—すなわち例外を呼び出しチェーンの上流へ「伝播」させることができます。
例外の伝播 は、メソッドが自分でエラーを処理せず、呼び出し側に「問題が起きたので、そちらで対処してください」と伝えるための仕組みです。
たとえ: あなたがコールセンターの担当者だとします。お客さんから自分では答えられない質問が来ました。あてずっぽうで答えるのではなく、「専門担当におつなぎします」と伝えます。質問を「上流へ回す(伝播させる)」わけです。
キーワード throws:どのように動作するか
Java では、メソッド宣言でキーワード throws を使って例外の伝播を示します。
構文:
tip_vozvrashchaemogo_znacheniya imyaMetoda(...) throws ExceptionType
{
// メソッド本体
}
throws の後には、そのメソッド内で発生し得る例外型を列挙します。これは他の開発者への注意喚起です。「注意! このメソッドはこの型の例外をスローする可能性があります。備えてください」
例:
public void readFile(String filename) throws FileNotFoundException
{
FileReader reader = new FileReader(filename); // FileNotFoundException をスローする可能性がある
// ...
}
ここでは readFile メソッドは自分でエラーを処理せず、「FileNotFoundException をスローするかもしれないので、呼び出し元で対処してほしい」と宣言しています。
2. 呼び出し側はどう対応すべきか?
throws を宣言しているメソッドを呼び出す場合、対応は2つあります:
- try-catch で処理する
- 自分のメソッドでも throws を付けてさらに伝播させる
方法 1: try-catch で処理する
public static void main(String[] args)
{
try
{
readFile("data.txt");
}
catch (FileNotFoundException e)
{
System.out.println("ファイルが見つかりません: " + e.getMessage());
}
}
ここでは例外を捕捉し、何をするか(たとえばユーザーにメッセージを表示する)を自分で決めています。
方法 2: さらに伝播させる
public static void main(String[] args) throws FileNotFoundException
{
readFile("data.txt");
}
この場合、エラー処理の責任は main を呼び出す側(通常は JVM)に移ります。処理されないままだと、プログラムはエラーメッセージを出して終了します。
3. 例: throws を用いたファイル読み取り
IOException と main レベルでの処理を含む完全な例:
import java.io.*;
public class FileDemo
{
// このメソッドは IOException をスローする可能性があることを宣言する
public static void printFirstLine(String filename) throws IOException
{
BufferedReader reader = new BufferedReader(new FileReader(filename));
String line = reader.readLine();
System.out.println("最初の行: " + line);
reader.close();
}
public static void main(String[] args)
{
try
{
printFirstLine("nofile.txt");
}
catch (IOException e)
{
System.out.println("ファイル読み取り時のエラー: " + e.getMessage());
}
}
}
メソッド printFirstLine は、ファイルがないときにどうすべきかを知りません—例外をそのまま伝播します。main 側で例外を捕捉してメッセージを表示します。
4. 有用なポイント
いつ、なぜ例外の伝播を使うのか?
- メソッドがエラー処理を決められない/決めるべきでない場合(ライブラリコードなど)。
- エラー処理がコンテキストに依存する場合(あるケースでは終了、別のケースでは別ファイルを試すなど)。
- 不要な try-catch でコードを汚さないため。
ベストプラクティス: 意味のある処理ができないなら例外は伝播させましょう。形だけの捕捉は避けてください。
複数の例外をスロー宣言できる
public void process() throws IOException, SQLException
{
// ...
}
チェック例外と非チェック例外の復習
チェック例外(例: IOException、SQLException)は、コンパイラが処理または伝播(throws)の宣言を要求します。
非チェック例外(例: NullPointerException、IllegalArgumentException)は、コンパイラが処理を要求しないため、throws に明記する必要はありません。
5. 例外を伝播させる際の典型的なミス
エラー № 1: チェック例外に対する throws の宣言を忘れる。
メソッドがチェック例外をスローし得るのに、throws に記載せず、かつ try-catch でも処理していない場合、コンパイラはエラーを報告します。
エラー № 2: 例外を捕捉するが、何もしない。
catch (Exception e) {} は悪いプラクティスです! 対処法がないなら、むしろ例外を上位へ伝播させましょう。
エラー № 3: あまりに広い型でスロー宣言する。
メソッドが FileNotFoundException しか起こし得ないのに、throws Exception と書くのは、コード理解を難しくします。
エラー № 4: 非チェック例外を throws に列挙する。
throws に NullPointerException などをわざわざ書く意味はありません—コンパイラはそれを要求せず、エラー処理の助けにもなりません。
GO TO FULL VERSION