Null について立ち止まって考える
Java には null
という概念がある。
これは何もないことを表す。
数字における 0 と同じようでいて、少し考え方が異なる。コンテキストによっては同じものを指すこともある。それが混乱だったり、認識の齟齬を生む原因だったりして、モダンな言語( Swift や Kotlin など)では、この null
という概念を扱うための言語仕様が用意されていることが多い。
例えば、モバイルアプリにおいて、次のようなのユーザ名を保持する User
class があるとする。
class User {
String name;
}
これを API 経由で取得して画面に表示する場合に、以下のような疑似コードを用意した。
UserData data = api.getUserData();
User user = new User(data.name);
View view.name = user.name;
このコードには null
について考える必要がある箇所がいくつあるだろうか。
- api が
null
だったら - data が
null
だったら - data.name が
null
だったら - user が
null
だったら - user.name が
null
だったら - view が
null
だったら - view.name が
null
だったら
7 つもある。これについて、全てについて立ち止まって考える必要があるというお話。
このコードをリリースしたところ、クライアントからユーザを表示するとアプリがクラッシュするのですぐに対応して欲しいと言われた。コードを次のように修正した。
try {
UserData data = api.getUserData();
User user = new User(data.name);
View view.name = user.name;
} catch (Exception e) {
// アプリがクラッシュしないようにする
}
アプリはクラッシュしなくなり、クライアントからは素早い対応で感謝された。素晴らしい!
クライアントからはその後、問い合わせはこないものの、
アプリを利用しているユーザからは、自身の情報が表示されないことがあって、非常に不満であり、
このサービスは永くは続きませんでした。
極端ですが、今回の例だと、こういったシナリオも想定され得る対応になっています。
この対応は、多くの異常な状態を隠蔽し、あたかも正常に動いているかのように見せてしまっています。
このような対応を null
の握りつぶし と呼んだりします。
では、どうしたら良かったでしょうか?
クライアントから最初の報告をもらった時に時間を巻き戻してみます。
エラーのログを見ると、 data.name
が null
になっていることがわかりました。
そこでコードを次のように修正しました。
UserData data = api.getUserData();
if (data.name != null) {
User user = new User(data.name);
View view.name = user.name;
}
アプリはクラッシュしなくなり、クライアントからは素早い対応で感謝された。素晴らしい!
しばらくすると、またアプリがクラッシュするとクライアントから至急の対応を依頼されました。
エラーログを見ると、今度は data
が null
になっていることがわかりました。
そこでコードを次のように修正しました。
UserData data = api.getUserData();
if (data != null && data.name != null) {
User user = new User(data.name);
View view.name = user.name;
}
アプリはクラッシュしなくなり、クライアントからは素早い対応で感謝された。素晴らしい!
クライアントからはその後、問い合わせはこないものの、
今回の対応でもアプリを利用しているユーザからは、自身の情報が表示されないことがあって、非常に不満であり、結局このサービスは永くは続きませんでした。
今回の例でも、こういったシナリオも想定され得る対応になっています。
この対応は、局所的な null
を考慮した対応をしたものの、コミュニケーションが不足しており、この対応でも、あたかも正常に動いているかのように見せてしまっています。
このような対応でも null
の握りつぶし と呼ばれてしまいます。
では、どうしたら良かったでしょうか?
立ち止まって考えるポイント
- なぜ
data
やdata.name
がnull
になってしまったのか -> こういったケースでは、 API 仕様へのフィードバックが必要で、そちらのチームとコミュニケーションが必要 -
data
やdata.name
がnull
の場合は、画面にどういった表示をしたいか -> プロダクトオーナーに確認し、そういったケースは想定しているのかコミュニケーションが必要- その状態をエラーとして扱うのか
- エラーとして扱うならどのレベルか(アラートの表示やデフォルト表示等)
そうすると、次のような要件が聞き出せました。
-
data
がnull
の場合はサーバ側に異常が発生しているので、エラー表示と問い合わせボタンを表示して欲しい -
data.name
がnull
の場合は名前が未登録だとあり得るので、エラーとはせずに、デフォルトで「匿名ユーザ」と表示して欲しい
次のようにコードで表現しました。
UserData data = api.getUserData();
if (data == null) {
showError();
return;
}
User user = new User(if(data.name == null) ? "匿名ユーザ" : data.name);
View view.name = user.name;
今までと異なり、コミュニケーションが発生したため、すぐには修正はできませんでしたが、
クラッシュもなくなり、ユーザからも問題があった場合に問い合わせを受け付けることができ、
クライアントからも頻繁に追加要望が出てくるようになり、
サービスは永く続くことになりました。めでたしめでたし。
続編。
アプリ内部ロジックにおいても安易に null
を返さない
例外を返そう
さもないと状態の異常を握り潰してしまい、本当の問題が隠蔽されて、そこで発生する課題の解決が困難になる
Java 魂で読んだかもしれない