はじめに
この実験では、Java のコレクションを扱う際に、null 値を効果的に処理する方法を学びます。コレクションは基本的なデータ構造であり、コレクション変数が null である可能性のある状況を安全に管理できる堅牢なコードを書くことは非常に重要です。null のコレクションをテストする方法、包括的な検証のために null チェックと空チェックを組み合わせる方法、および強化された null 安全性のために Optional クラスを活用する方法を探索します。最終的には、一般的な NullPointerException エラーを回避するのに役立ちます。
コレクションが null かどうかをテストする
このステップでは、Java のコレクションを扱う際に null 値をどのように処理するかを探ります。List や Set などのコレクションは基本的なデータ構造であり、コレクション変数が null である可能性のある状況を安全に処理できるコードを書くことは非常に重要です。
Java で null 値とは、変数が何らかのオブジェクトを参照していないことを意味します。null オブジェクトのメソッドやプロパティにアクセスしようとすると、プログラムは NullPointerException でクラッシュします。これは Java で非常に一般的なエラーであり、回避方法を学ぶことは不可欠です。
まずは、この問題を示す簡単な Java プログラムを作成してみましょう。
WebIDE エディタで
HelloJava.javaファイルが開いていない場合は、開きます。ファイルの内容全体を以下のコードに置き換えます。
import java.util.List; public class HelloJava { public static void main(String[] args) { List<String> names = null; // Intentionally set to null // This line will cause a NullPointerException if names is null // int size = names.size(); // System.out.println("Size of the list: " + size); System.out.println("Program finished."); } }このコードでは、
namesという文字列のListを宣言し、明示的にnullに設定しています。コメントアウトされた行は、nullのリストに対してsize()メソッドを呼び出そうとした場合に何が起こるかを示しています。これはNullPointerExceptionを引き起こします。ファイルを保存します(Ctrl+S または Cmd+S)。
次に、プログラムをコンパイルしましょう。WebIDE の下部にあるターミナルを開き、
~/projectディレクトリにいることを確認します。以下のコマンドを実行します。javac HelloJava.javaコンパイルが成功すると、何も出力されないはずです。
次に、プログラムを実行します。
java HelloJava以下の出力が表示されるはずです。
Program finished.NullPointerExceptionを引き起こす行がコメントアウトされているため、プログラムはクラッシュすることなく実行されます。
では、コレクションを使用する前に null をチェックするようにコードを修正しましょう。
エディタで再度
HelloJava.javaを開きます。mainメソッドを修正して、null チェックを追加します。import java.util.List; public class HelloJava { public static void main(String[] args) { List<String> names = null; // Intentionally set to null if (names != null) { // This code will only run if names is NOT null int size = names.size(); System.out.println("Size of the list: " + size); } else { System.out.println("The list is null."); } System.out.println("Program finished."); } }namesがnullでないかどうかをチェックするif文を追加しました(names != null)。リストのサイズを取得して表示するコードは、このifブロック内にあります。つまり、namesが有効なリストオブジェクトである場合にのみ実行されます。elseブロックは、namesがnullの場合を処理します。ファイルを保存します。
修正したプログラムをコンパイルします。
javac HelloJava.java再度プログラムを実行します。
java HelloJava今回は、以下の出力が表示されるはずです。
The list is null. Program finished.プログラムはリストが
nullであることを正しく識別し、適切なメッセージを表示し、NullPointerExceptionを回避しました。
この単純な if (collection != null) チェックは、コレクションを扱う際に NullPointerException を防ぐ最も基本的な方法です。これは Java プログラミングで頻繁に使用する基本的なテクニックです。
null チェックと空チェックを組み合わせる
前のステップでは、コレクションが null かどうかをチェックする方法を学びました。しかし、コレクションは null でなくても 空(要素を含まない)場合があります。多くの場合、null のコレクションと空のコレクションを同じように扱いたい、または少なくとも両方の可能性を処理したいことがあります。
コレクションが空かどうかのチェックは、isEmpty() メソッドを使用して行います。このメソッドは、コレクションに要素が含まれていない場合は true を返し、それ以外の場合は false を返します。
null のリストと空のリストの違いを示し、その後チェックを組み合わせるようにプログラムを修正してみましょう。
WebIDE エディタで
HelloJava.javaファイルを開きます。内容を以下のコードに置き換えます。
import java.util.List; import java.util.ArrayList; // Import ArrayList public class HelloJava { public static void main(String[] args) { List<String> nullList = null; // Intentionally set to null List<String> emptyList = new ArrayList<>(); // An empty list System.out.println("Checking nullList:"); if (nullList != null) { System.out.println("nullList is not null."); if (nullList.isEmpty()) { System.out.println("nullList is empty."); } else { System.out.println("nullList is not empty."); } } else { System.out.println("nullList is null."); } System.out.println("\nChecking emptyList:"); if (emptyList != null) { System.out.println("emptyList is not null."); if (emptyList.isEmpty()) { System.out.println("emptyList is empty."); } else { System.out.println("emptyList is not empty."); } } else { System.out.println("emptyList is null."); } System.out.println("\nProgram finished."); } }新しい空の
ArrayListとして初期化されるemptyListを追加しました。そして、nullListとemptyListの両方に対して同じ null チェックと空チェックを行い、出力の違いを確認します。ファイルを保存します。
ターミナルでプログラムをコンパイルします。
javac HelloJava.javaプログラムを実行します。
java HelloJava以下のような出力が表示されるはずです。
Checking nullList: nullList is null. Checking emptyList: emptyList is not null. emptyList is empty. Program finished.この出力から、
nullListがnullであり、emptyListはnullではないが空であることが明確にわかります。
では、null チェックと空チェックを 1 つの条件に組み合わせましょう。一般的なパターンは、コレクションが null または空であるかどうかをチェックすることです。
エディタで
HelloJava.javaを開きます。mainメソッドを修正してチェックを組み合わせます。import java.util.List; import java.util.ArrayList; public class HelloJava { public static void main(String[] args) { List<String> names = null; // Can be null or an empty list // Combined check: is names null OR is names empty? if (names == null || names.isEmpty()) { System.out.println("The list is null or empty."); } else { System.out.println("The list is not null and not empty."); // You can safely iterate or access elements here // For example: // System.out.println("First element: " + names.get(0)); } // Let's test with an empty list List<String> anotherList = new ArrayList<>(); System.out.println("\nChecking anotherList (empty):"); if (anotherList == null || anotherList.isEmpty()) { System.out.println("anotherList is null or empty."); } else { System.out.println("anotherList is not null and not empty."); } // Let's test with a non-empty list List<String> populatedList = new ArrayList<>(); populatedList.add("Item 1"); System.out.println("\nChecking populatedList (not empty):"); if (populatedList == null || populatedList.isEmpty()) { System.out.println("populatedList is null or empty."); } else { System.out.println("populatedList is not null and not empty."); } System.out.println("\nProgram finished."); } }論理和演算子 (
||) を使用して、names == nullとnames.isEmpty()の条件を組み合わせています。どちらかの条件が真の場合、ifブロックが実行されます。また、空のリストと要素があるリストでテストを追加し、組み合わせたチェックがどのように動作するかを確認します。重要な注意:
names == null || names.isEmpty()の条件の順序は重要です。namesがnullの場合、条件の最初の部分 (names == null) は真です。Java の短絡評価により、2 番目の部分 (names.isEmpty()) は 評価されません。これにより、NullPointerExceptionが防止されます。もしnames.isEmpty() || names == nullと書き、namesがnullだった場合、names.isEmpty()を呼び出すとNullPointerExceptionが発生します。オブジェクトに対する他のチェックと組み合わせる場合は、常にnullを 最初に チェックしてください。ファイルを保存します。
プログラムをコンパイルします。
javac HelloJava.javaプログラムを実行します。
java HelloJava以下のような出力が表示されるはずです。
The list is null or empty. Checking anotherList (empty): anotherList is null or empty. Checking populatedList (not empty): populatedList is not null and not empty. Program finished.これは、組み合わせたチェックが
nullと空のリストを正しく識別し、要素があるリストと区別する方法を示しています。この組み合わせたチェックは、nullまたは空である可能性のあるコレクションを処理する非常に一般的で安全な方法です。
Null 安全のために Optional を使用する
このステップでは、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 の可能性が減少します。
まとめ
この実験では、Java で null のコレクションを処理して NullPointerException を防ぐ方法を学びました。まず、null のコレクションに対してメソッドを呼び出すことで実行時エラーが発生する問題を示しました。
次に、コレクションの要素やプロパティにアクセスする前に、コレクションが null かどうかを安全にチェックするさまざまな手法を探りました。これは、堅牢でエラーのない Java コードを書くための基本的なスキルです。



