オブジェクトの参照がnullの場合、nullチェックをせずに参照しようとすると、NullPointerException
が発生する問題がありました。
Java8からはjava.util.Optional
を使うことで、この問題を回避できるようになります。
従来のnullチェック
!=
などでnullチェックをしてNullPointerException
を回避していました。
Java
String str = null;
//nullではない場合、値を出力する。
if(str != null){
System.out.println(str);
}
これからのnullチェック
nullでない場合だけ処理を行うように制御する。
Java
String str = null;
Optional.ofNullable(str).ifPresent(s -> System.out.println(s));
Optional型オブジェクトの生成
Optional型のオブジェクトを生成する方法にはOptional.of(XXX)
を使う方法とOptional.ofNullable(XXX)
を使う方法の2種類がありますが、後者を使用することをおすすめします。理由は、Optional.of(XXX)
を使うとXXXがnullだった場合、java.lang.NullPointerException
が発生してしまうからです。(後述)
Optional.of(XXX)
を使う方法
Java
// Optional<String>型のオブジェクトを生成と出力
Optional<String> str = Optional.of("aiueo");
str.ifPresent(s -> System.out.println(s)); //aiueo
// Optional<Integer>型のオブジェクトを生成と出力
Optional<Integer> num = Optional.of(100);
num.ifPresent(s -> System.out.println(s)); //100
// Optional<Boolean>型のオブジェクトを生成と出力
Optional<Boolean> bool = Optional.of(true);
bool.ifPresent(s -> System.out.println(s)); //true
// Optional<String[]>型のオブジェクトを生成と出力
String[] array = { "apple", "banana", "lemon" };
Optional<String[]> data = Optional.of(array);
// 配列で返すなら以下
data.ifPresent(s -> System.out.println(Arrays.toString(s)));// [apple, banana, lemon]
// 個別で返すなら以下
data.map(Arrays::stream).orElse(Stream.empty()).forEach(System.out::println);
//apple
//banana
//lemon
// 以下の書き方でも可能。
data.ifPresent(s -> Arrays.stream(s).forEach(a -> System.out.println(a)));
//apple
//banana
//lemon
しかし、この書き方の場合、以下のように値がnullだった場合、java.lang.NullPointerException
が発生してしまいます。
Java
// Optional<String>型のオブジェクトを生成と出力
String tmp = null;
Optional<String> str = Optional.of(tmp); //ここでNullPointerExceptionが発生。
str.ifPresent(s -> System.out.println(s));
そこでこの問題を回避するためOptional.ofNullable(XXX)
を用います。
Optional.ofNullable(XXX)
を使う方法
Optional.ofNullable(XXX)
に渡される値がnullであっても実行時エラーが発生することはありません。
Java
// Optional<String>型のオブジェクトを生成と出力
String tmp = null;
Optional<String> str = Optional.ofNullable(tmp);
str.ifPresent(s -> System.out.println(s));
ifPresentメソッド
ifPresent
メソッドは、Optional型オブジェクトに値が存在する場合(=!null)のみ、処理を実行することができます。
処理はラムダ式で記述します。なお、結果を返却することはできません。
結果を返却したい場合は、後述のmap
メソッドを使用します。
Java
public static void main(String args[]) {
Integer num = 100;
Optional.ofNullable(num).ifPresent(n -> {
n = n + 100;
int result = calc(n);
System.out.println(result); //40000
});
}
private static int calc(Integer n) {
return n * n;
}
ifPresent
メソッドの中で別メソッドを呼び出したい場合は以下のように記述します。
Java
public static void main(String args[]) {
Integer num = 100;
Optional.ofNullable(num).ifPresent(n -> print(n));
}
private static void print(Integer n) {
System.out.println(n); //100
}
mapメソッド
ifPresent
メソッドは値を返さないため、値を返したい場合はmap
メソッドを使用します。
nullの場合
変数str
にはnull
が代入されているのでmap(s -> s + "_OK")
の部分は実行されず、.orElse("なし");
の実行結果が返却されます。
Java
String str = null;
Optional<String> strOpt = Optional.ofNullable(str);
String result = strOpt.map(s -> s + "_OK").orElse("なし");
System.out.println("結果:" + result); //結果:なし
null以外の場合
変数str
にはtest
が代入されているのでnullではありません。よってmap(s -> s + "_OK")
の実行結果が返却されます。
Java
String str = "test";
Optional<String> strOpt = Optional.ofNullable(str);
String result = strOpt.map(s -> s + "_OK").orElse("なし");
System.out.println("結果:" + result); //結果:test_OK
空文字の場合
変数str
が空文字の場合、nullではないので、map(s -> s + "_OK")
の実行結果が返却されます。
Java
String str = "";
Optional<String> strOpt = Optional.ofNullable(str);
String result = strOpt.map(s -> s + "_OK").orElse("なし");
System.out.println("結果:" + result); //結果:_OK
mapメソッドで別メソッドを呼び出す
mapメソッド内で別メソッドを実行するには以下のように記述します。
nullではないときに.map(i -> calc(i))
の部分が実行されるため、calcメソッド内はの変数はnullでないことが保証されています。
Java
public static void main(String[] args) {
// 初期値を設定
Integer num = 100;
// Optional型に変換
Optional<Integer> intOpt = Optional.ofNullable(num);
// nullの場合は0を返却。null以外の場合はcalc()の実行結果を返却。
Integer result = intOpt.map(i -> calc(i)).orElse(0);
// 結果
System.out.println("結果:" + result);// 結果:10000
}
private static Integer calc(Integer i) {
return i * i;
}
orElseメソッド
orElse
はOptional型オブジェクトがnullの場合に結果を返却しますが、null以外の場合でも処理自体は実行されているので注意が必要です。null以外の場合に
orElse
が実行されるとマズイ場合は、後述のorElseGet
を使用しましょう。Optional型オブジェクト | 挙動 |
---|---|
nullの場合 | orElse メソッドの処理を実行して結果を返却する。 |
null以外の場合 | orElse メソッドの処理を実行するだけ。結果は返却しない。 |
orElseメソッドで別メソッドを呼び出す。
orElseメソッド内で別メソッドを実行するには以下のように記述します。
普通にメソッド名を記述するだけでOKです。
Java
public static void main(String[] args) {
// 初期値を設定
Integer num = null;
// Optional型に変換
Optional<Integer> intOpt = Optional.ofNullable(num);
// nullの場合はinit()の実行結果を返却。null以外の場合はcalc()の実行結果を返却。
Integer result = intOpt.map(i -> calc(i)).orElse(init());
// 結果
System.out.println("結果:" + result);// 結果:0
}
private static Integer init() {
return 0;
}
private static Integer calc(Integer i) {
return i * i;
}
orElseメソッドはnull以外の場合も実行されている
以下の処理、CNTの値はいくつが出力されるでしょう?答えは2
です。
intOpt
がnull
ではないにもかかわらず、.orElse(init())
の部分が実行され、init
メソッドの中でCNT
がインクリメントされてしまっています。
つまり、OptionalクラスのorElseメソッドはnull以外の場合も実行されているのです。
Java
public class App {
static Integer I = 1;
public static void main(String[] args) {
// 初期値を設定
Integer num = 100;
// Optional型に変換
Optional<Integer> intOpt = Optional.ofNullable(num);
// nullの場合は0を返却。null以外の場合はcalcの実行結果を返却。
Integer result = intOpt.map(i -> calc(i)).orElse(init());
// 結果
System.out.println("結果:" + result);// 結果:10000
// CNTはいくつ?
System.out.println(I); //2
}
private static Integer init() {
CNT++;
return CNT;
}
private static Integer calc(Integer i) {
return i * i;
}
}
orElseGetメソッド
.orElseGetは、Optionalがnullの場合のみ実行されます。null以外の場合は実行されません。
Java
public class App {
static Integer CNT = 1;
public static void main(String[] args) {
// 初期値を設定
Integer num = 100;
// Optional型に変換
Optional<Integer> intOpt = Optional.ofNullable(num);
// nullの場合は0を返却。null以外の場合はcalcの実行結果を返却。
Integer result = intOpt.map(i -> calc(i)).orElseGet(() -> init());
// 結果
System.out.println("結果:" + result);// 結果:10000
// CNTはいくつ?
System.out.println(CNT); //1
}
private static Integer init() {
CNT++;
return CNT;
}
private static Integer calc(Integer i) {
return i * i;
}
}
以上で記事の解説はお終い!
もっとJavaやSpringを勉強したい方にはUdemyがオススメ!同僚に差をつけよう!