Optional を使用した null 安全なコーディング
このステップでは、Java 8 で導入された Optional
クラスを使用して、Java で潜在的な null
値を処理するより現代的なアプローチを探ります。Optional
は、null ではない値を含む場合と含まない場合があるコンテナオブジェクトです。値の存在または不在をより明示的に表現する方法を提供し、NullPointerException
のリスクを減らすのに役立ちます。
if (collection != null)
のチェックは多くの状況で完全に有効で必要ですが、Optional
を使用すると、特に値を返す場合と null
を返す場合があるメソッドを扱うときに、コードがより読みやすく表現力豊かになります。
コレクションで Optional
をどのように使用できるか見てみましょう。Optional
は通常、単一の値に使用されますが、メソッドが Optional<List<SomeObject>>
を返すシナリオに遭遇することがあります。
-
WebIDE エディタで HelloJava.java
ファイルを開きます。
-
内容を、潜在的に null のリストで Optional
を使用する例を示す以下のコードに置き換えます。
import java.util.List;
import java.util.ArrayList;
import java.util.Optional; // Import Optional
public class HelloJava {
// A method that might return an Optional containing a list, or an empty Optional
public static Optional<List<String>> getNames(boolean includeNames) {
if (includeNames) {
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
return Optional.of(names); // Return an Optional containing the list
} else {
return Optional.empty(); // Return an empty Optional
}
}
public static void main(String[] args) {
// Case 1: Get names when includeNames is true
Optional<List<String>> namesOptional1 = getNames(true);
System.out.println("Checking namesOptional1:");
// Check if the Optional contains a value
if (namesOptional1.isPresent()) {
List<String> names = namesOptional1.get(); // Get the list from the Optional
System.out.println("List is present. Size: " + names.size());
// You can also check if the list itself is empty
if (names.isEmpty()) {
System.out.println("List is empty.");
} else {
System.out.println("List is not empty. First name: " + names.get(0));
}
} else {
System.out.println("List is not present (Optional is empty).");
}
System.out.println("---");
// Case 2: Get names when includeNames is false
Optional<List<String>> namesOptional2 = getNames(false);
System.out.println("Checking namesOptional2:");
if (namesOptional2.isPresent()) {
List<String> names = namesOptional2.get();
System.out.println("List is present. Size: " + names.size());
if (names.isEmpty()) {
System.out.println("List is empty.");
} else {
System.out.println("List is not empty. First name: " + names.get(0));
}
} else {
System.out.println("List is not present (Optional is empty).");
}
System.out.println("\nProgram finished.");
}
}
このコードでは:
getNames
メソッドを定義して、Optional<List<String>>
を返します。このメソッドは、リストを取得する場合と何も取得しない場合(空の Optional
で表される)のシナリオをシミュレートします。
main
メソッドで、getNames
を true
と false
で呼び出して、両方のケースをテストします。
namesOptional.isPresent()
を使用して、Optional
にリストが含まれているかどうかをチェックします。
isPresent()
が true の場合、namesOptional.get()
を使用してリストを取得します。これは、存在をすでにチェックしているため安全です。
isPresent()
ブロック内で、names.isEmpty()
などのリスト自体のチェックを実行できます。
-
ファイルを保存します。
-
ターミナルでプログラムをコンパイルします。
javac HelloJava.java
-
プログラムを実行します。
java HelloJava
以下のような出力が表示されるはずです。
Checking namesOptional1:
List is present. Size: 2
List is not empty. First name: Alice
---
Checking namesOptional2:
List is not present (Optional is empty).
Program finished.
この出力は、Optional
がリストがまったく返されない場合をどのように処理するのに役立つかを示しています。
Optional
は、値が存在しない場合を処理するための他の便利なメソッドも提供します。例えば:
orElse(defaultValue)
: 値が存在する場合はその値を返し、そうでない場合はデフォルト値を返します。
orElseGet(supplier)
: 値が存在する場合はその値を返し、そうでない場合は supplier
関数の結果を返します。
orElseThrow(exceptionSupplier)
: 値が存在する場合はその値を返し、そうでない場合は exceptionSupplier
によって生成される例外をスローします。
ifPresent(consumer)
: 値が存在する場合、指定されたアクションを実行します。
リストが存在する場合により簡潔に処理するために、ifPresent
を使用するようにコードを修正してみましょう。
-
エディタで HelloJava.java
を開きます。
-
main
メソッドを修正して ifPresent
を使用するようにします。
import java.util.List;
import java.util.ArrayList;
import java.util.Optional;
public class HelloJava {
public static Optional<List<String>> getNames(boolean includeNames) {
if (includeNames) {
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
return Optional.of(names);
} else {
return Optional.empty();
}
}
public static void main(String[] args) {
// Case 1: Get names when includeNames is true
Optional<List<String>> namesOptional1 = getNames(true);
System.out.println("Checking namesOptional1 using ifPresent:");
namesOptional1.ifPresent(names -> {
// This block only runs if namesOptional1 contains a list
System.out.println("List is present. Size: " + names.size());
if (names.isEmpty()) {
System.out.println("List is empty.");
} else {
System.out.println("List is not empty. First name: " + names.get(0));
}
});
if (!namesOptional1.isPresent()) { // Still need a check if you need to handle the absence case
System.out.println("List is not present (Optional is empty).");
}
System.out.println("---");
// Case 2: Get names when includeNames is false
Optional<List<String>> namesOptional2 = getNames(false);
System.out.println("Checking namesOptional2 using ifPresent:");
namesOptional2.ifPresent(names -> {
System.out.println("List is present. Size: " + names.size());
if (names.isEmpty()) {
System.out.println("List is empty.");
} else {
System.out.println("List is not empty. First name: " + names.get(0));
}
});
if (!namesOptional2.isPresent()) {
System.out.println("List is not present (Optional is empty).");
}
System.out.println("\nProgram finished.");
}
}
if (namesOptional.isPresent()) { ... namesOptional.get() ... }
構造を namesOptional.ifPresent(names -> { ... })
に置き換えました。ラムダ式 (names -> { ... }
) 内のコードは、Optional
に値が含まれている場合にのみ実行されます。ifPresent
は値が存在する場合のみを処理するため、Optional
が空の場合を処理するために if (!namesOptional.isPresent())
チェックを追加しています。
-
ファイルを保存します。
-
プログラムをコンパイルします。
javac HelloJava.java
-
プログラムを実行します。
java HelloJava
出力は前と同じになるはずで、ifPresent
が Optional
内の値の存在を処理する代替方法を提供することを示しています。
Optional
を使用すると、値が存在しない可能性に関するコードの意図が明確になり、その不在を明示的に処理するように促されるため、予期しない NullPointerException
の可能性が減少します。