71
69

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Javaの検査例外の存在意義

Last updated at Posted at 2014-04-17

あらゆるところで否定され続けているJavaの検査例外だが、なぜこのような仕組みが必要なのか説明しようと思う。

この投稿は golang で複数のエラーをハンドリングする方法 という記事を見かけたので書いた。

いつも通り「コメントは大歓迎」です。

検査例外の話

検査例外の目的は 静的な分岐の強制 である。

例えばこのようなコードがあったとする。

f = File.open("dummy.txt")
f.write "Hello"
f.close

このようなAPIではプログラマが「ファイルを開けなかったときの処理を忘れる」ことを防ぐことができない。

一方、検査例外を使えば「開けなかったときの処理」をプログラマが忘れることができない。
忘れた場合はコンパイルエラーになり、そもそも実行できないからである。
(簡単のためRuby風なAPIにしている)

try {
  File f = File.open("dummy.txt");
  f.write("Hello");
  f.close();
} catch (IOException e) { // catchしない場合コンパイルエラー
  System.out.println("ファイルを開けませんでした。");
}

Maybe/Eitherの話

検査例外というのは、関数型言語におけるMaybeEitherの一種だと考えるのが良い。
型システムを利用して、失敗したときの処理を書き忘れるのを防ぐのが目的である。

Scala風なプログラムで、分岐を強制している例を挙げる。

val result = File.open("dummy.txt")
result match {
  case Success(f) => f.write("Hello")
  case Fail       => System.out.println("ファイルを開けませんでした。")
}

このように分岐後でしか f に触れることができず、分岐を強制させることができる。

Kotlinの話

Kotlinでは「nullになる可能性がある変数」かそうでないかを明確に区別する。 参考
最近Facebookが発表したHackにも同様の機能があるようだ。

val file : File? = File.open("dummy.txt") // 型は明示的に書いているだけで省略可能
file.write("Hello") // コンパイルエラー

// OKな例
val file : File? = File.open("dummy.txt")
if (file != null) {
  file.write("Hello") // ここのfileの型は<File?>ではなく<File>
} else {
  print("ファイルを開けませんでした。");
}

通常の言語において、変数 file の型はずっと変わらないが、この言語ではifチェックの前後で型が異なるように振る舞う。

Goの話

(断わっておくが、Goのことはよく知らないので、間違いがあればコメントがほしい)

if file, err := file_open("dummy.txt"); err != nil {
  print("ファイルを開けませんでした。");
} else {
  file.write("Hello");
}

// 無視する例
file, _ := file_open("dummy.txt");
file.write("Hello");

Goは失敗時に二値を返すという方針のようだ。
値が二つある以上、「分岐を忘れる」ことは少なくなるが、強制はできないだろう。
また、正常系でも異常系でもfile, errに触れるのも問題である。

C#の話

これはC#でも同様である。C#では例外以外にはTryXxxを使うのが推奨されている。

File file;
if (File.TryOpen("dummy.txt", out file)) {
  file.Write("Hello");
} else {
  Console.WriteLine("ファイルを開けませんでした。");
}

// 無視する例
File file;
File.TryOpen("dummy.txt", out file);
file.Write("Hello");

関数は bool を返すため、分岐を忘れることは少なくなるが、強制できるわけではない。

まとめ

検査例外はそんなに悪い機能じゃない
コンパイルエラーを有効に使おう

71
69
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
71
69

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?