オブジェクトの参照がnullの場合、nullチェックをせずに参照しようとすると、NullPointerException
が発生する問題がありました。
Java8からはjava.util.Optional
を使うことで、この問題を回避できるようになります。
!=
などでnullチェックをしてNullPointerException
を回避していました。
Java
String str = null;
//nullではない場合、値を出力する。
if(str != null){
System.out.println(str);
}
nullでない場合だけ処理を行うように制御する。
Java
String str = null;
Optional.ofNullable(str).ifPresent(s -> System.out.println(s));
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
メソッドは、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
}
ifPresent
メソッドは値を返さないため、値を返したい場合はmap
メソッドを使用します。
変数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); //結果:なし
変数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メソッド内で別メソッドを実行するには以下のように記述します。
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
はOptional型オブジェクトがnullの場合に結果を返却しますが、null以外の場合でも処理自体は実行されているので注意が必要です。orElse
が実行されるとマズイ場合は、後述のorElseGet
を使用しましょう。Optional型オブジェクト | 挙動 |
---|---|
nullの場合 | orElse メソッドの処理を実行して結果を返却する。 |
null以外の場合 | 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;
}
以下の処理、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は、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がオススメ!同僚に差をつけよう!