java8には、Optionalクラスが新しくサポートされました。長らくnullと付き合ってきたjava開発者にとっては、まさに画期的な発明に見えるでしょう。
Optionalとは
javaでは、長らくの間、nullという概念に悩まされてきました。代表的なのがnullチェックであり、以下のようなコードが多用されていました。
String nullableObject = "nullable";
if(nullableObject != null){
System.out.pringln(nullableObject); // nullable
}
いわゆる「nullチェック」と呼ばれるものです。nullが入る可能性のある変数では、毎回のように記述されてきました。かなり冗長な印象を受けますし、スムーズなコードリーディングを阻害するので、出来れば書きたくない代物だったのです。
そこで悩んだある開発者は専用のメソッドを作り、ある開発者はユーティリティクラスを作り、またある開発者はラッパーオブジェクトを作り…、といったアプローチが試みられてきました。
しかし、java8にOptional型が実装されたことにより、nullに対するアプローチに一定のインターフェースが出来ました。
Optional型を使うことのメリット
前述したコードが、こうなります。
Optional<String> optional = Optional.ofNullable("nullable");
optional.ifPresent(System.out::println); // nullable
ラムダ式を使うことによって簡潔な表現が可能となりました。
ラムダ式については、以下の記事でも詳しく解説していますので、よければお読みください。
Optionalの使いどころ
関数型っぽい書き方が出来るようになるOptionalですが、nullチェック程度の置換ですとかえって煩雑になります。
もし、変数に対しデフォルト値を設定したい場合は、有効な手段となりうるかもしれません。たとえば以下です。
Optional<String> optional = Optional.ofNullable(null);
String s = optional.orElse("elseString"); // nullが格納されていたら引数の文字列を返す
System.out.println(s); // elseString
これにより、「nullのときのデフォルト値」を定義することができます。
ところで、ラムダ式の匿名関数は、その関数の外側までスコープとなりますので、private変数は難なく読み込むことが出来るのですが、再代入できない点に注意してください。
例えば、以下のコードはコンパイルエラーになります。
int notNullCount = 0;
Optional<String> optional = Optional.ofNullable("nullable");
optional.ifPresent(o -> notNullCount++); // コンパイルエラー
しかし、以下のようにインスタンスの関数やフィールドへのアクセスは可能です。インスタンスメンバであれば、代入も可能です。
List<String> list = new ArrayList<>();
Optional<String> optional = Optional.ofNullable("nullable");
optional.ifPresent(o -> list.add(o));
System.out.println(list); // [nullable]
isPresent()、get()は使わない
isPresent()は、中身がnullでなければtrueを返すメソッドです。これを使うようでは、nullチェックを行うのと変わりませんので、Optionalを使う意味はないと言えます。
get()は、値を取り出すメソッドですが、こちらもnullのときにNoSuchElementException
がスローされます。これもわざわざOptionalで行う必要はありません。
使用する判断
非常に難しいところですが、メソッドの戻り値でOptionalを返すのは有効な手段でしょう。逆に、変数宣言などでOptionalインスタンスを作るパターンはあまり無いのではないでしょうか。
よくあるDB上の検索を行うfindById()メソッドをOptional実装することを考えてみます。
Main.java
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
User.findById(4).ifPresent(u -> System.out.println(u.id));
}
}
User.java
import java.util.*;
public class User{
public Integer id = 4;
public static Optional<User> findById(Integer id){
User user = null;
if(id == 4){
user = new User();
}
return Optional.ofNullable(user);
}
}
取得したモデルに対して何か処理を行う場合には、非常に簡潔なコーディングになります。
とはいえ、規約である程度縛らないと使い物になりませんし、全てOptionalを返していては必要のない箇所まで余計なコーディングが必要になりますから、使用の判断は適切に下さなければなりません。
まとめ
まだまだ使いどころの難しいインターフェースですが、今後研究が行われてベストプラクティスが生み出されると思います。
現段階での使用は好みによるところも大きいですが、次第にこなれて使い勝手のよいものとなる可能性もありますので、目を離さずに見守りたいものですね。