9-4. 演習問題
問題1:選択問題
Section titled “問題1:選択問題”1-1. 次のコードで finally ブロックが実行されるのはどの場合か。
try { // 何らかの処理} catch (RuntimeException e) { System.out.println("catch");} finally { System.out.println("finally");}- A. 例外が発生しなかった場合のみ
- B. 例外が発生した場合のみ
- C.
catchブロックが実行された場合のみ - D. 例外の有無にかかわらず常に実行される
解答と解説
正解:D
finally ブロックは try ブロックの処理が終わった後、例外の有無にかかわらず必ず実行される(System.exit() が呼ばれた場合などを除く)。
- 例外なし:
try→finally - 例外あり・catch可能:
try→catch→finally - 例外あり・catchなし:
try→finally→ 例外が伝播
この特性から、ファイルやDB接続などのリソース解放に finally が使われてきた。現代では try-with-resources を使う方が安全で簡潔だ。
1-2. Optional<T> に関する記述として正しいものはどれか。
- A.
Optional.of(null)は空の Optional を返す - B.
Optional.get()は値が空でも null を返す - C.
Optional.orElse()は値が空の場合に指定したデフォルト値を返す - D. Optional はフィールドに持つことが推奨される
解答と解説
正解:C
- A:
Optional.of(null)はNullPointerExceptionをスローする。nullを許容するならOptional.ofNullable(null)を使う - B:
Optional.get()は値が空ならNoSuchElementExceptionをスローする - C:正しい。
opt.orElse("default")は値があればその値、空なら"default"を返す - D:Optional をフィールドに持つのはアンチパターン。シリアライズできず、設計の意図も伝わりにくい。フィールドは
nullで持ち、アクセサメソッドでOptionalを返すのが推奨
1-3. 次のコードの問題として最も適切なものはどれか。
public void processUser(String name, int age) { try { if (name == null) throw new NullPointerException(); if (age < 0) throw new Exception("年齢が不正"); // 処理 } catch (Exception e) { System.out.println("エラー: " + e.getMessage()); }}- A.
try-catchを使うべきでない - B. 引数バリデーションに例外を使うべきでなく、またキャッチする例外が広すぎる
- C.
Exceptionをキャッチすることは常に正しい - D. 問題はない
解答と解説
正解:B
問題点1:引数バリデーションに try 内で例外を生成するのは冗長
引数のバリデーションは try-catch なしでシンプルに書く:
if (name == null) throw new IllegalArgumentException("nameはnullにできません");if (age < 0) throw new IllegalArgumentException("ageは0以上である必要があります");問題点2:Exception を広くキャッチして無視している
catch (Exception e) は NullPointerException も OutOfMemoryError 以外のほぼ全ての例外を捕捉する。意図しない例外も飲み込んでしまい、バグが見えなくなる。具体的な例外クラスでキャッチするか、エラーを適切に処理する。
1-4. try-with-resources の説明として正しいものはどれか。
- A.
try内で生成したオブジェクトは全て自動クローズされる - B.
AutoCloseableを実装したリソースを自動的にクローズする - C.
finallyブロックの代わりに使う - D. Java 11以降でのみ使用できる
解答と解説
正解:B
- A:
tryの()内で宣言されたもの(AutoCloseable実装)のみ自動クローズされる - B:正しい。
()内にAutoCloseableを実装したオブジェクトを宣言すると、tryブロック終了時に自動でclose()が呼ばれる - C:
finallyが不要になるわけではなく、リソース管理に特化した構文 - D:Java 7以降で使用可能
// AutoCloseable の実装例class MyResource implements AutoCloseable { @Override public void close() { System.out.println("リソースをクローズ"); }}
try (MyResource r = new MyResource()) { // 処理} // ← r.close() が自動的に呼ばれる問題2:穴埋め問題
Section titled “問題2:穴埋め問題”2. 次のコードの( )に入るコードを答えよ。
// (1) 引数バリデーションに適切な例外を使うpublic void setAge(int age) { if (age < 0 || age > 150) { throw new (1)("年齢が不正な値です: " + age); } this.age = age;}
// (2) Optional を使ってnullを安全に処理public Optional<String> findEmail(int userId) { String email = database.get(userId); // nullかもしれない return Optional.(2)(email);}
// (3) try-with-resources でファイルを読むtry (3) FileReader reader = new FileReader("data.txt") ){ // 処理} catch (IOException e) { e.printStackTrace();}
// (4) Optional の値がある場合だけ処理を実行Optional<String> name = Optional.of("Alice");name.(4)(n -> System.out.println("Hello, " + n));解答と解説
IllegalArgumentExceptionofNullable((かっこの開き)ifPresent
解説
(1) 引数の不正チェックには IllegalArgumentException(java.lang パッケージ、import不要)を使う。プログラムのバグを示す非検査例外であり、呼び出し元に誤った引数を渡していることを知らせる。
(2) Optional.ofNullable(value) は引数が null なら Optional.empty() を、そうでなければ Optional.of(value) を返す。null かもしれない値を Optional にラップする場合に使う。
(3) try-with-resources の構文は try (リソース宣言) { 処理 }。( と ) でリソースを囲む。
(4) ifPresent(Consumer) は Optional に値があれば Consumer を実行し、空なら何もしない。
問題3:記述問題
Section titled “問題3:記述問題”3-1. 次のコードを、適切な例外処理とガード節を使ってリファクタリングせよ。
public String getUserEmail(int userId) { if (userId > 0) { User user = userRepository.findById(userId); if (user != null) { if (user.getEmail() != null) { return user.getEmail(); } else { return "メールアドレス未設定"; } } else { return "ユーザーが存在しません"; } } else { return "無効なユーザーID"; }}解答と解説
public String getUserEmail(int userId) { if (userId <= 0) throw new IllegalArgumentException("無効なユーザーID: " + userId);
User user = userRepository.findById(userId); if (user == null) throw new UserNotFoundException(userId);
return Optional.ofNullable(user.getEmail()) .orElse("メールアドレス未設定");}改善点:
- ガード節:条件を反転させて早期リターン(例外スロー)することでネストを削減
- 不正な入力は例外でシグナルを送る:不正なIDは文字列で返すより例外の方が呼び出し元が適切に処理できる
- Optional の活用:
nullの場合のデフォルト値はorElseで簡潔に表現
3-2. UserService クラスで「ユーザーが見つからない」場合の独自例外クラス UserNotFoundException を実装し、findById メソッドで使用するコードを書け。
解答と解説
// 独自例外クラスpublic class UserNotFoundException extends RuntimeException { private final int userId;
public UserNotFoundException(int userId) { super("ユーザーが見つかりません: ID=" + userId); this.userId = userId; }
public int getUserId() { return userId; }}
// UserService での使用public class UserService { private final Map<Integer, User> userStore = new HashMap<>();
public User findById(int userId) { User user = userStore.get(userId); if (user == null) { throw new UserNotFoundException(userId); } return user; }
// Optional を返すバリエーション(nullを許容するAPIの場合) public Optional<User> findByIdOptional(int userId) { return Optional.ofNullable(userStore.get(userId)); }}
// 呼び出し元try { User user = service.findById(999);} catch (UserNotFoundException e) { System.err.println(e.getMessage()); System.err.println("対象ID: " + e.getUserId());}独自例外に userId フィールドを持たせることで、エラーハンドリング時に「どのIDで失敗したか」を型安全に取り出せる。
問題4:ハンズオン
Section titled “問題4:ハンズオン”下の Java WASM プレイグラウンドで Calculator を完成させ、例外変換と Optional の使い分けを確認せよ。
取り組む内容
Section titled “取り組む内容”divideでゼロ除算を明示的に検出し、ArithmeticExceptionを投げるparseAndAddでNumberFormatExceptionをIllegalArgumentExceptionに変換するsafeDivideでは例外ではなくOptional.empty()を返すmainのテストケースを増やし、正常系と異常系の両方を観察する
確認したいポイント
Section titled “確認したいポイント”- 「例外で止める」ケースと「Optional で表現する」ケースをどう使い分けるか
- 例外メッセージに入力値を含めると調査しやすくなる理由
try-catchは必要な場所だけに絞るべき理由