到達点は、失敗の種類を分け、後始末を確実にすることです。前提は第07回です。
失敗を成功に見せない
「検索結果がない」「文字列を数値にできない」「ファイルを読めない」は違う状態です。通常の不在ならOptional、呼び出し規約の違反やI/O失敗なら例外が候補になります。例外を捕まえて空の結果を返すだけだと、障害と正常な0件を区別できなくなります。
import java.io.*;
import java.util.OptionalInt;
public class Main {
static OptionalInt parseMinutes(String text) {
try {
int value = Integer.parseInt(text.strip());
return value >= 0 ? OptionalInt.of(value) : OptionalInt.empty();
} catch (NumberFormatException e) {
return OptionalInt.empty();
}
}
public static void main(String[] args) throws IOException {
try (BufferedReader reader = new BufferedReader(new StringReader("25\nabc\n"))) {
String line;
while ((line = reader.readLine()) != null) {
OptionalInt result = parseMinutes(line);
System.out.println(result.isPresent() ? result.getAsInt() : "入力が不正");
}
}
}
}
Main.javaで実行すると25と「入力が不正」が出ます。このメソッドでは負の数と数値でない入力を、どちらも「変換できない」という一種類にまとめる契約にしました。理由を画面へ細かく返す必要があるなら、別の結果型を設計します。nullを受け付ける契約にはしていません。
捕まえる場所と閉じる場所
checked exceptionは呼び出し側に処理またはthrows宣言を要求します。RuntimeExceptionの派生はその要求を受けません。ただし、チェックされるかどうかと、無視してよいかどうかは別問題です。
try-with-resourcesはAutoCloseableな資源をスコープ終了時に閉じます。ファイル、データベース接続などに有効です。例外で途中終了しても後始末されます。エラーを別の例外へ変換するときは、元の例外をcauseとして渡し、原因を追えるようにします。
Optionalのgetを無条件に呼べば、不在時に失敗します。orElse は代替値を先に評価し、orElseGet は必要時に計算します。重い代替処理には違いが出ます。
練習と解答
練習:「0」「-1」「abc」「 12 」を分類し、ファイルの読み取り失敗を同じ「入力が不正」にしてよいか考えます。
解答:0と12は有効、残りは不正です。読み取り失敗まで同じにすると、利用者は入力を直しても解決できません。外側でI/O失敗を扱い、再試行や終了を判断します。
公式資料
例外とOptional APIを参照できます。
Java全20回の目次 | 前の回 | 次の回