飽きっぽい人のブログ

プログラマとしてもテスターとしても中途半端な人のブログ

Javaのエラー設計は何が悪かったのか

先日TwitterJavaの検査例外ってそこまで悪くないのになといった旨の発言をしたら結構反応あったのでまとめることにする。
今回はJavaの言語機能のエラー設計の何が悪かったか書く。
ちなみに私のJava知識は十年以上前に学校で習ったきりなので何も知らないに等しい。検査例外という名称もつい先日知ったばかり。そのため妄想で書いてしまうことがあるので、もし間違っていたらプロのマサカリを希望する。 なおTwitter上では多くの方が私の疑問に答えてくださり助かりました。この場をもってお礼申し上げます。

throws指定しなくても投げることのできる例外が存在する

いわゆる非検査例外と呼ばれているものであり、catchやthrowsで指定しなくてもコンパイルが通ってしまう例外。大昔にこれがあるからJavaの検査例外は意味がない的な批判を見た気がする。
確かにせっかく例外をcatchしてなかったらコンパイルエラーにする機能があるのに、catchしなくてもコンパイルエラーにならない例外が飛んでくるかも知れないと思うと安心できない。
とはいえ、ユーザーにcatchさせたくないエラーというのは少なからず存在するので、そういったものは例外という同じくくりにせずに、GoやRustのようにpanicとし、構文からエラーの扱いを別にするべきだったのではなかろうか。

高階関数と相性が悪い

高階関数を使うようにする機能として、FunctionやBiFunctionといったインターフェースが提供されているが、これらはapplyする時に例外が投げられるため、例外を投げることができない。
対応としてcatchしてもみ消したり、RuntimeExceptionで包んで投げたりしてる記事をいくつかみたが非常にめんどくさい。
これはJavaのthrowsが複数の型を指定できること、及びJavaのメソッドがthrowsでオーバーロードできないことに起因していると思われる。
仮の話ではあるが、高階関数でもthrowsできるように型システムやJava中間言語の設計をきちんと行っていれば発生しなかった問題のように感じる。

非同期との相性が悪い

らしい。今のJavaの非同期処理がどうなっているかよくわからないので正直詳しいことはあまり書けない。
C#だとasync/awaitで非同期処理で発生した例外をcatchできるので、検査例外の機能を維持しつつ非同期処理を書くといったことはできそうではあるが、今のJavaでそれをやるにはやはり型システムを大きく変えなければ実現できないように思う。
あと今後発生するかも知れない非同期処理のパラダイム変化に対応することを考えると、エラーは戻り値ベースにしてシンプルに実装しておいたほうが後々面倒にはならなさそうに感じる。

まとめ

以上がTwitterで教えてもらったりして、私がJavaのエラー設計の悪いと思うところを挙げたものである。
個人的には検査例外自体は悪い発想ではなかったが、Javaの型システムやその他の設計が時代についていけていないのが原因でイマイチな感じになっている印象。
そのため検査例外自体も低い評価になってるように見えるのでもったいないように感じる。
聞くところによるとSwiftが検査例外を採用しているらしい。時間があれば上手く落とし込めているか確かめてみたい。