1
0

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.

nullとOptional型と理想と現実

Posted at

最近、モバイルアプリの開発をするようになって、swiftとkotlinを勉強するようになった。
そんな中で初めて「Optional型」という概念に触れて「そんな便利な型が存在する言語があるのか」と思っていたのだが、よくよく調べてみたら、Java8にもOptional型があるというのを今更ながら知った。
以下はOptionalに関するJavaDoc。
https://docs.oracle.com/javase/jp/8/docs/api/java/util/Optional.html

Optional型とは

null(言語によってはnil)を格納できる変数の型のこと。
Javaの場合は、intbooleanなどプリミティブ型nullを格納できない(コンパイルエラー)が、IntegerStringなど参照型の変数はnullが格納されるかもしれないため、事前にnullチェックをしないと、NullPointerExceptionが発生するかもしれない。

Optional型のメリット

プログラム初心者の自分が最初に感じた疑問。

参照型もnull入るんだからOptional型ってわざわざ定義する必要なくね?

たしかに「nullになるかもしれない型」という意味ではOptional型も参照型も関係ない。重要なのは、参照型の場合はnullチェックしなければいけない処理で、それを怠ってしまいがちな点だろう。
nullチェックを怠った場合、NullPointerExceptionが発生するが、これは実行時例外に該当するため、最悪の場合は本番障害にならないと気づかないケースもありえる。

String str = getString();//これがnullを返した場合
//以下のif文を忘れてしまうとコンパイルは通るが、実行時にNullPointerExceptionに
//if(str == null){
//    str = "";
//}
return str.length();

対して、Optional型を使った場合は、専用のメソッドを使わなければ値を取り出せないため、必ずnullの場合を考慮したコーディングをしなければならない。
つまり、意図せずnullが入り込んだ場合にどうするかを見逃さなくなる点が一番のメリットだと思う。

Optional<String> opt = Optional.ofNullable(getString());
String str = opt.orElse(""); //Optionalから値を取り出すには、nullを考慮したメソッド`orElse`を使わないと取り出せない。
return str.length();

でも本来は想定外のnullをなくすべき

なぜだろう。最近すっかり忘れてしまっていたのだが、本来は想定外の事態が発生しても大丈夫なようにしておく(nullチェックをしておく)ことが大切なわけではなく、想定外の事態を作り込まない(nullが返却される原因をなくす)ことの方が重要だ。

先ほどのコードで例えるならば、

String str = getString();//そもそもgetStringがnullを返すことが悪いのでは?
//以下の処理は記述しておくべきだったけど、getString()がnullを返すなんて仕様書に載ってないじゃん
//if(str == null){
//    str = "";
//}
return str.length();

というケースのことだ。
nullを考慮すべき責任は誰(どのコード)にあるのかがはっきりしないまま、とりあえずnullチェックしておけば安全だからと安直に考えていないだろうか。

経験上、先例のgetStringに該当するモジュールは、他ベンダーによって大昔に作られた前提モジュールであったり、すでに稼働済みの他システムでも流用されているため、請け負った案件の保守費用などの関係から「改修できないモジュール」扱いにされていたりすることが多く、「設計としてあるべき」という思想ではなく「現実的に考えてこうするしか無い」という事情に合わせてコーディングされることが圧倒的に多い。

今回記事をまとめようと思ったきっかけは、最近自分自身も「現実的に考えてこうするしか無い」という事情に甘んじて、とりあえずnullチェックしておけば安全だからと安直に考えて実装するようになってしまったように感じたからだ。

本来は、意図的にnullにしているのではなく、プログラマの不注意によりnullが入り込む実装になったまま放置されているモジュールや、意図的にnullにしたことが周知されずに、あるいは時代を経て「意図的にnullにしたよ」という内容の引き継ぎが途絶えて「この場合にnullになるなんてありえない」と判断してしまう行為自体が悪なのだ。

まとめ

Optional型はnullの場合の考慮をプログラマに強いることができるが、本来nullをどう扱うべきなのかを設計する思想は忘れてはならない。(←自分への戒め)

たとえば、getString()nullを返すかもしれないモジュールであるならば、getString()の戻り値もOptionalにすべきである。

Optional<String> opt = getString();//Optional型を返すことを明示するべき
String str = opt.orElse("");
return str.length();

あるいは、getString()nullを返さないことが保証されるべきであるならば、getString()内部処理を修正して、呼び出し側はOptional型を使わなくてもいい。

String str = getString();//絶対nullではないことが保証されたなら
//以下のif文はデッドコードとなってしまうため、実装しないのが正しい
//if(str == null){
//    str = "";
//}
return str.length();

swiftやkotlinなどOptionalが使える新しい言語でも、この思想は忘れてはいけないだろう。

1
0
0

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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?