nullはJavaのWeakポイント

純粋にオブジェクト指向的に考えた場合、オブジェクト間の通信にnullを使用するなんてことはあってはならない事だ。

ある値がnullであるということは何かの状態を表しているわけで、その状態をnullで表してしまうと人間の頭にとっては扱い難い存在となる。

例えばあなたがコーヒーカップを持っているとしてコーヒーカップがnullだでは想像で補うことは可能であっても意味が通じない。状態をnullで表すのを止めてコーヒーカップが空だと言えば格段にわかりやすくなる。

ここで重要なのは名前がついているかどうか?ということだ。名前を付けることによって人間の頭は高速に処理できるようになる。名前を省略されるとそもそも状態があることを認識することが困難になる。

このことからnullはコンピュータのメモリの状態を表す言葉であって実装であるといえる。つまりnullはカプセル化の対象であって、インターフェース間でやりとりはしてはいけない存在ということになる。

Javaの話に戻ると、本来はnullを返したり渡したりしてはいけないのだけれど、標準のAPIがそうなっていなかったりでなんだかんだ言ってnullをやり取りすることになる。

そのときに良く行われるのが「不正な省略」だ。仮にオーダーを取得するメソッドがあったとして、それがNullableな場合の正しいメソッドのSignatureは次のようになる。

  public Order getOrderOrNull();

こんなダサいNamingはありえないので"OrNull"は省略されることになる。

  public Order getOrder();

これが「不正な省略」。NullableでないgetOrder()と見分けがつかなくなって、情報が明らかに欠落していることがわかる。このSignatureからいったい誰がこのメソッドがNullableだと認識することができるでしょう?コメントに書いたりasserを使ったりソースを覗いたり(知らない間にNullableに修正される可能性もある)手間のかかる不確実な手段を使わざるを得ない結果、Nullableであることが見落とされやすい。

そしてこのnullの状態に対して特別な実装を一つでも見逃すとそれは即バグになる。Nullableであるかどうかという情報は非常に重要だという状況とは裏腹にソースコード上では見えづらくなっている。しかもそれに対する仕組みが実装されていない(assertやJSR-305では不十分)ので結果としてnullに対する実装が見逃されやすい現実がある。

これがnull周りでバグが多い理由だ。Javaに限らず現在主流になっているオブジェクト指向言語のほとんどに共通する弱点。

ScalaみたいにOption[Order],Orderで使い分ければ情報が正確にソースに残ることになるので、Nullableであるかどうかなんて完全に忘れてしまっても問題ないけど、Javaのようにそういった仕組みがない場合はNullableかどうかを全部頭の中に記憶しながらコードを書くことになる。結果、頭の中で余計なコストを消費することになる。

ここで議論になるのがインターフェースがどうあるべきか?という問題。動的言語のようにNullableどころか型情報まで省略するののがいいのか。静的言語のようにできるだけ情報を載せるようにしたほうがいいのか?って所。そこに行くと長くなりそうなので省略。

で、結論としてはオブジェクト指向言語ならば、nullを渡したりreturnした時点でRuntimeエラーになるのが正しいのだけれども既存の言語はそうはなっていないので対処法を列挙しておく。

・できるだけnullを返さない、渡さないように実装する。
Stringなら空文字で扱ったり、Listなら空のリストを返すようにしたりなど、nullで表現しなくてもよさそうな所は別の表現を選択する。ScalaみたいなOptionのようなオブジェクトを使ってもいいし、is〜のような問い合わせメソッドに置きかえれるのも多いはず。ただしこの辺の対策はパフォーマンスへの影響を気を配る必要がある。

・OrNullを付けるようにする。
Nullableなメソッド名やパラメータにOrNullを付けるようにするバカバカしくはあるけけど簡単に実行できて確実にバグは減る。要は家中のあらゆる箇所に「nullを忘れるな」と張り紙するようなもので、nullに対する実装の見落としや設計の問題を発見しやすくなる。それでもダメなら救いようがなかったとあきらめるしかない。

リテラルを先に書くのを止める
リテラルを先に書くスタイルは先にあげた「不正な省略」の一種だ。OrNullを付けるのとは逆にNullに対する実装を忘れやすくなる。設計に問題があったとしても発見できる確率も減る。