なんかピンと来なかった
Scalaで初めてOption
型に出会い、その時に「なぜOption
を使うのか?」を調べてもなかなかピンと来なくて、いまOptional
を使う利点の一つに気がつけたので、書こうと思います。
なぜnull
がダメなのか
初めて出てきた概念だったので、「Optional
の利点」ばかり調べていました。
ですが、「null
のダメな理由」分かったとき「なぜOptional
を使うのか」が分かってきました。
僕が思うnull
が悪い理由は「null
がどんな型にもなれる」ことです。
String str = null;
Integer num = null;
int[] array = null;
なぜ「null
がどんな型にもなれる」ことが悪いかというと、「メソッドが呼び出せる保証が失われる」からです。
具体的に言えば、String
なら文字の長さが取得できるlength()
が使える保証があるはずです。ですが、str
がnullであれば、length()
は呼び出せず、エラーしてしまいます。
str.length();
このエラーの原因を「コンパイル時に型エラーとして検出することができない」ことが問題なのだと思います。
まとめると、str
がString
型ならString
型が持っているlength()
, charAt()
などのメソッドが呼び出せる保証していると思います。
str
がnull
なら、メソッドが呼びだせずにエラーし、メソッドが呼び出せる保証を壊してしまいます。そしてこれをコンパイル時にはチェックできず、実行している最中にいきなりエラー(Javaならヌルポ)が発生します。
Optionalを使う理由
「コンパイル時に型エラーとして検出することができない」ことが問題なので、「コンパイル時に型のエラーとして検出できる」ようにすればいいと思います。
そこで、型エラーとして検出できるように新しい型を導入したくなります。
「あること」と「ないこと」を表す型の導入です。これが「Optional
型」だと思います。
つまり、Optional
を使う理由はnull
が抱える問題の「コンパイル時に型のエラーとして検出できない」ことを解決することだと思います。
Optionalを使ってみる
List
をインデックスで指定した要素を取得するメソッドを作ることにします(Javaで書きます)
注意点は、指定した「要素がない場合」があることです。この「要素がない場合を」null
で扱った方法が以下の方法です。
null
を使ってしまう方法
String getAt(List<String> list, int index){
// indexがlistの範囲のとき
if(0 <= index && index < list.size()){
String elem = list.get(index);
return elem;
} else {
return null; // 範囲外なのでnullを返す
}
}
使うときはこんな感じです
String elem = getAt(list, 9); // nullが返るかもしれない
null
が戻り値の問題点
要素がないときは「Stringなnull
」が返ってきてしまいます。このgetAt()
を使うときはnull
が返ってくることを常に意識してプログラムを書く必要があります。
一度null
が返るかもしれないメソッドを呼べば、その戻り値を使うたびにnull
であることを常に意識があります。
Optionalを使う
Optional<String> getAtOpt(List<String> list, int index){
// indexがlistの範囲のとき
if(0 <= index && index < list.size()){
String elem = list.get(index);
return Optional.ofNullable(elem); // elemをOptionalで包んで返す
} else {
return Optional.empty(); // ないので、empty()を返す
}
}
使うときはこんな感じなります
Optional<String> elemOpt = getAtOpt(list, 9);
Optional
が戻り値の利点
「null
を使ってしまう方法」とは違い、「null
が返ってくることを意識してプログラムを書く必要がない」です。
他の利点として、戻り値のOptional<String>
が「あるかも」しれない「ないかも」しれないことを表すドキュメントみたいになっているように見えることです。他の人が作った関数でも、戻り値がOptional
なら「結果がないかもれないんだなぁ」とすぐに分かります。それに対し、null
を返す場合があることはソースコードを実際に読まないと分からないです。
おまけ - 値があるときに実行
ちなみに、Optional
の値があるときに実行させたければ、isPresent
が便利です
elemOpt.ifPresent(elem -> {
System.out.println(elem); // elemOptの中身があるときに中身elemを表示
});
まとめ
null
はどんな型にでもなれる
↓
null
の時はメソッドが呼び出せる保証が失われる
(コンパイル時にチェックできない)
↓
コンパイル時にチェックできるように
「ある」、「ない」を表す新しい型を導入したくなる
↓
「ある」、「ない」を表すOptional
の導入