Javaでnullを完全に理解するための9つの方法

Javaでnullを処理するための9つの具体的な方法とサンプルコードJava
この記事は約20分で読めます。

※本記事のコンテンツは、利用目的を問わずご活用いただけます。実務経験10000時間以上のエンジニアが監修しており、基礎知識があれば初心者にも理解していただけるように、常に解説内容のわかりやすさや記事の品質に注力しております。不具合・分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。(理解できない部分などの個別相談も無償で承っております)
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

この記事を読めばnullを完全に理解し、Javaでのnull問題を効果的に解決できるようになります。

プログラミングを始めたばかりの方、またはJavaでnullによって困っているという方は、ぜひともこの記事を最後までお読みください。

9つの具体的な手法とサンプルコードを交えて、nullとは何か、どう扱えばよいのかを解説していきます。

●nullとは何か

nullはプログラミング言語でよく見かける特殊な値の一つです。

しかし、これが何であるか、またなぜ必要なのかは初心者には少し難しいテーマでもあります。

○nullの定義

nullとは、オブジェクトが存在しない、もしくは未定義である状態を表す特別な値です。

Javaではnullは、どのオブジェクト型の変数にも代入することができます。

つまり、参照型の変数が実際に何も参照していない状態を表現する手段として用いられます。

String str = null; // strはnull値を持っている
Object obj = null; // objもnull値を持っている

○nullが生まれるシナリオ

nullがどのような状況で出現するのか、いくつかの一般的なシナリオを見てみましょう。

  1. 変数を宣言したが、まだ初期化していない。
  2. メソッドが何らかの理由でオブジェクトを返せなかった。
  3. 外部リソースとの通信が失敗した。
  4. データベースからのクエリが何も返さなかった。
  5. 判定の結果として、特定の条件下でnullを代入した。

上記のようなシナリオでnullが発生する可能性があります。プログラミングにおいて、これらのシナリオは非常に一般的です。

しかし、nullが発生すると、それを適切に処理しないと「NullPointerException」というランタイムエラーが発生する可能性があります。

このような事態を防ぐためにも、nullの正確な理解と適切な取り扱いが必要です。

●Javaでのnullの扱い

nullとは何か、どのような状況で出現するかについての理解が深まったところで、Javaでのnullの扱い方について解説します。Javaにおいてもnullは非常に特殊な存在です。

プログラムが期待する挙動とは異なる動きを見せることがあります。

この部分では、Javaでnullがどのような問題を引き起こすのか、またそれをどう防ぐかについて詳しく見ていきます。

○nullが問題を引き起こす例

Javaで特によく見かけるnullに関連する問題は、NullPointerExceptionです。

これは、nullの参照を持つ変数に対してオブジェクトとしての操作(メソッド呼び出しやフィールドアクセスなど)を行ったときに発生します。

例として、文字列を大文字に変換する処理があった場合に、null値を持つ変数が大文字変換メソッドに渡されたとしましょう。

public class NullExample {
    public static void main(String[] args) {
        String str = null;
        String upperStr = str.toUpperCase();  // ここでNullPointerExceptionが発生
        System.out.println(upperStr);
    }
}

このコードではstr変数がnullを持っています。

toUpperCase()メソッドを呼び出す行でNullPointerExceptionが発生します。

このエラーが発生すると、プログラムはその場で停止してしまいます。

○nullと比較する正しい方法

nullが問題を引き起こす一例を見たところで、それをどのように防ぐかについて話を進めます。

Javaではnullとの比較は非常によく行われますが、その際には注意が必要です。

下記のコードは、if文を使ってnullをチェックしています。

public class NullCheckExample {
    public static void main(String[] args) {
        String str = null;

        // 正しいnullチェック
        if (str == null) {
            System.out.println("strはnullです");
        } else {
            // strがnullでなければ、この部分が実行される
            String upperStr = str.toUpperCase();
            System.out.println(upperStr);
        }
    }
}

このコードを実行すると、strはnullですという出力が得られます。

つまり、nullチェックが正しく行われ、toUpperCase()メソッドの呼び出しは行われません。

こうすることで、NullPointerExceptionを防ぐことができます。

●基本的なnull判定の方法

nullの存在はJavaプログラミングにおいても無視できない現実です。

そこで、ここではJavaでnullをどう判定するかについて、基本的な方法をいくつか紹介します。

○サンプルコード1:if文でのnull判定

if文はJavaで非常によく使われる制御構造の一つです。

null判定でもif文は頻繁に用いられます。

public class NullCheckWithIf {
    public static void main(String[] args) {
        String str = null;

        // null判定
        if (str == null) {
            System.out.println("strはnullです");
        } else {
            System.out.println("strはnullではありません。値は:" + str);
        }
    }
}

このコードではstr変数にnullを代入し、その後にif文でnull判定を行っています。

str == nullという条件式でstrがnullかどうかをチェックし、結果に応じてコンソールにメッセージを出力します。

コードを実行すると、strはnullですと出力されます。

このif文の使い方でnullを簡単かつ明確に判定できます。

○サンプルコード2:三項演算子でのnull判定

三項演算子もnull判定に使える非常に便利な演算子です。

構文は条件式 ? 値1 : 値2となります。条件式がtrueの場合には値1が、falseの場合には値2が選ばれます。

public class NullCheckWithTernary {
    public static void main(String[] args) {
        String str = null;

        // null判定とメッセージの設定
        String message = (str == null) ? "strはnullです" : "strはnullではありません。値は:" + str;

        System.out.println(message);
    }
}

このコードでは、三項演算子を用いてstr変数がnullかどうかを判定し、それに応じてmessage変数にメッセージを設定しています。

このコードの実行結果も前述のif文を用いた場合と同様に、strはnullですと出力されます。

●オプショナルクラスを使用したnull判定

Java 8から導入されたOptionalクラスは、nullによる例外や複雑な条件分岐を避けるための素晴らしい手段です。

Optionalを使うことで、nullとの戦い方が多少楽になります。

○サンプルコード3:Optionalクラスの基本的な使い方

Optionalクラスを使い始めるには、まずjava.util.Optionalパッケージをインポートする必要があります。

import java.util.Optional;

public class OptionalBasicExample {
    public static void main(String[] args) {
        // Optionalオブジェクトの生成
        Optional<String> optional = Optional.ofNullable(null);

        // nullチェックと値の取得
        String result = optional.orElse("デフォルト値");
        System.out.println("結果: " + result);
    }
}

このコードではOptional.ofNullableメソッドを用いて、nullかもしれない値をOptionalオブジェクトとしてラップしています。

その後、orElseメソッドを使ってnullかどうかをチェックし、nullの場合は”デフォルト値”という文字列を取得しています。

このコードを実行すると、”結果: デフォルト値”と出力されることが確認できます。

このようにOptionalクラスを使うことで、null判定をよりシンプルに、読みやすく書くことができます。

○サンプルコード4:Optionalでnullを安全に処理

Optionalクラスを使えば、より高度なnull処理も可能です。

mapメソッドとorElseメソッドを組み合わせた例を紹介します。

import java.util.Optional;

public class OptionalAdvancedExample {
    public static void main(String[] args) {
        Optional<String> optional = Optional.ofNullable("hello");

        // nullでなければ処理を行い、nullならデフォルト値
        String result = optional.map(String::toUpperCase).orElse("デフォルト値");
        System.out.println("結果: " + result);
    }
}

このサンプルコードでは、Optional.mapメソッドを使用しています。

このメソッドはOptionalオブジェクトがnullでない場合に、指定した関数(この場合はString::toUpperCase)を適用します。

そして、nullの場合はorElseメソッドで指定したデフォルト値を使用します。

このコードを実行すると、”結果: HELLO”と出力されます。

もしOptionalオブジェクトがnullであれば、”デフォルト値”が出力される仕組みです。

●Java 8+の機能を使ったnull処理

Java 8以降のバージョンでは、null処理をより効率的かつ安全に行うための新しい機能が多く導入されています。

ここでは、特にStream APIとOptionalクラスの組み合わせを用いたnull処理に焦点を当てます。

○サンプルコード5:Stream APIでnullをフィルタ

Java 8以降で利用できるStream APIを使えば、配列やコレクション内のnullを簡単にフィルタリングすることができます。

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamFilterNull {
    public static void main(String[] args) {
        List<String> listWithNulls = Arrays.asList("Java", null, "Python", null, "Ruby");

        // Stream APIでnullを除去
        List<String> filteredList = listWithNulls.stream()
                                                 .filter(item -> item != null)
                                                 .collect(Collectors.toList());

        // 結果を出力
        System.out.println(filteredList);
    }
}

このコードでは、まずList<String>型の変数listWithNullsにnullが含まれるリストを用意しています。

その後、stream()メソッドを使用してストリームに変換し、filter()メソッドでnullでない要素だけを抽出して新しいリストfilteredListに保存しています。

このコードを実行すると、コンソールには[Java, Python, Ruby]と表示され、nullがしっかりとフィルタリングされたことがわかります。

○サンプルコード6:OptionalとStreamの組み合わせ

OptionalクラスとStream APIを組み合わせることで、null安全なコードをさらに強化することができます。

import java.util.Optional;
import java.util.stream.Stream;

public class OptionalAndStream {
    public static void main(String[] args) {
        Stream<Optional<String>> optionals = Stream.of(Optional.of("Java"), Optional.empty(), Optional.of("Python"));

        // OptionalとStreamを組み合わせてnull安全な処理
        String result = optionals.filter(Optional::isPresent)
                                 .map(Optional::get)
                                 .collect(Collectors.joining(", "));

        // 結果を出力
        System.out.println("結果: " + result);
    }
}

こちらのコードでは、Stream<Optional<String>>という形で、Optionalオブジェクトを含むストリームを用意しています。

このストリーム内でnull(Optional.empty())を安全に処理するために、filter()map()を駆使しています。

このコードを実行すると、コンソールには結果: Java, Pythonと表示されます。

Optional.empty()が存在しても、その影響を受けずに安全に処理を進めています。

●ライブラリを使ったnull判定

Javaの標準機能だけでなく、外部ライブラリを活用することでもnull処理をより便利かつ安全に行うことができます。

特にApache Commons LangとGoogle Guavaは、null処理に関する多くの便利なメソッドを持っています。

○サンプルコード7:Apache Commons Langを使用したnull安全なコード

Apache Commons LangライブラリにはStringUtilsクラスがあり、このクラスはnullを安全に扱うための多数のメソッドを持っています。

下記のコードは、StringUtilsを使ってnull安全な文字列操作を行う一例です。

import org.apache.commons.lang3.StringUtils;

public class NullSafeWithApache {
    public static void main(String[] args) {
        String str = null;

        // StringUtilsを使用してnull安全に文字列を大文字に変換
        String upperCase = StringUtils.upperCase(str);

        // 結果の出力
        System.out.println("大文字に変換した結果:" + upperCase);
    }
}

このコードでは、StringUtils.upperCaseメソッドを用いてnull値であるstrを大文字に変換しようとしています。

StringUtilsクラスのメソッドはnullに対して安全であるため、このコードはエラーを引き起こしません。

このコードを実行すると、コンソールには大文字に変換した結果:nullと表示され、プログラムは正常に終了します。

○サンプルコード8:Google Guavaでのnull処理

Google Guavaもまた、null安全なコードを書くための多くの便利なメソッドを提供しています。

GuavaのPreconditionsクラスを用いてnullチェックを行う一例を紹介します。

import com.google.common.base.Preconditions;

public class NullSafeWithGuava {
    public static void main(String[] args) {
        String str = null;

        try {
            // GuavaのPreconditionsを用いたnullチェック
            Preconditions.checkNotNull(str, "文字列がnullです");
        } catch (NullPointerException e) {
            // 結果の出力
            System.out.println(e.getMessage());
        }
    }
}

このコードでは、Preconditions.checkNotNullメソッドを用いてstrがnullかどうかをチェックしています。

もしstrがnullであれば、第二引数で指定したエラーメッセージと共にNullPointerExceptionがスローされます。

このコードを実行すると、文字列がnullですというメッセージがコンソールに出力されます。

●nullの注意点と対処法

null処理の妥当性は、プログラムの安全性と可読性に大きく影響します。

ここでは、Javaにおいてnullを効果的に扱うための各種注意点とそれに対する対処法を詳しく解説します。

○nullを引数として渡す際の注意

nullをメソッドに引数として渡す場合、そのメソッド内でnullチェックがされていないと、NullPointerExceptionが発生する可能性があります。

これは非常によくある問題です。

public class NullArgumentExample {
    public static void main(String[] args) {
        printLength(null);
    }

    public static void printLength(String str) {
        // nullチェックがされていない
        System.out.println("文字列の長さ: " + str.length());
    }
}

このコードでは、printLengthメソッドがnullを受け入れる可能性がありますが、nullチェックがされていないため、実行するとNullPointerExceptionが発生します。

□実行後のコンソール出力

NullPointerExceptionが発生し、プログラムが異常終了します。

□対処法

引数がnullである可能性がある場合は、メソッドの冒頭でnullチェックを行いましょう。

public static void printLength(String str) {
    // nullチェック
    if (str == null) {
        System.out.println("引数がnullです");
        return;
    }
    System.out.println("文字列の長さ: " + str.length());
}

○nullを返値とするメソッドの対処法

メソッドがnullを返す可能性がある場合、それを受け取る側もnullを適切に処理する必要があります。

public class NullReturnExample {
    public static void main(String[] args) {
        String result = getString();
        // nullチェックがされていない
        System.out.println("取得した文字列の長さ: " + result.length());
    }

    public static String getString() {
        return null;
    }
}

このコードでは、getStringメソッドがnullを返す構造になっています。

そのため、その後のresult.length()の呼び出しでNullPointerExceptionが発生します。

□実行後のコンソール出力

NullPointerExceptionが発生し、プログラムが異常終了します。

□対処法

返値がnullである可能性がある場合、nullチェックを行いましょう。

if (result != null) {
    System.out.println("取得した文字列の長さ: " + result.length());
} else {
    System.out.println("取得した文字列はnullです");
}

●テストコードでnullをチェックする方法

プログラムを書く際、nullの扱いは避けられないテーマとなります。

特に、メソッドの動作を保証するためには、nullに対するテストも重要です。

JUnitを活用すれば、nullに関する問題を未然に防ぐことができます。

○サンプルコード9:JUnitを使ったnullのテストケース

JUnitを使用してnullをチェックするためのサンプルテストコードを紹介します。

この例では、文字列の長さを取得するメソッドをテストしています。

import org.junit.Test;
import static org.junit.Assert.*;

public class StringTest {

    @Test
    public void testStringLengthWithNonNull() {
        String str = "Java";
        assertEquals(4, getStringLength(str));
    }

    @Test(expected = NullPointerException.class)
    public void testStringLengthWithNull() {
        getStringLength(null);
    }

    public static int getStringLength(String str) {
        return str.length();
    }
}

このテストコードでは、getStringLengthメソッドが非nullの文字列を受け取った場合と、nullを受け取った場合の2つのシナリオをテストしています。

testStringLengthWithNullメソッドでは、expected属性を使用してNullPointerExceptionが発生することを期待しています。

テストを実行すると、testStringLengthWithNonNullは正常にパスしますが、testStringLengthWithNullは期待通りのNullPointerExceptionが発生し、テストは成功となります。

今回のテストコードでは、Javaの基本的なテストフレームワークであるJUnitを利用しています。

nullの扱いに関する問題は、テストコードを書くことで予防することができます。

まとめ

Javaプログラミングにおけるnullの扱いは、初心者から上級者までが注意深く対応しなければならない重要なテーマです。

この記事では、nullの基本的な概念から、その判定方法、ライブラリを用いた高度なテクニックまで、多角的に解説しました。

nullに関する深い理解と適切な処理方法を身につけることは、プログラミングスキル全体の向上にも寄与します。

この記事が、nullという課題に対するあなたの理解を一歩前に進める手助けになれば幸いです。