Java で Character オブジェクトが null かどうかをチェックする方法

JavaBeginner
オンラインで実践に進む

はじめに

この実験では、Java の Character ラッパークラスを使用する際に、潜在的な null 値をどのように扱うかを探ります。プリミティブな char 型とは異なり、Character オブジェクトは null になる可能性があり、これを適切に扱わないと NullPointerException エラーが発生することがあります。Character オブジェクトが null かどうかを検証する方法、null チェックと他の文字プロパティチェックを組み合わせる方法、およびより安全な null 処理のために Optional クラスを利用する方法を学びます。

実際の例を通じて、null の Character オブジェクトを効果的に管理し、一般的なランタイムエラーを防ぎ、アプリケーションの信頼性を向上させる堅牢な Java コードを書く実践的な経験を積むことができます。

Character ラッパーの null チェックを行う

このステップでは、Java の Character ラッパークラスを使用する際に、潜在的な null 値をどのように扱うかを探ります。プリミティブな char 型は null になることができませんが、オブジェクトである Character ラッパークラスは null になる可能性があります。null 値を適切に扱うことは、Java でよく見られるプログラムをクラッシュさせる可能性のある NullPointerException エラーを防ぐために重要です。

まず、null の Character オブジェクトに対してメソッドを呼び出そうとすると NullPointerException が発生することを示す簡単な Java プログラムを作成します。

  1. WebIDE エディタで HelloJava.java ファイルが開いていない場合は、開きます。

  2. ファイルの内容全体を次のコードに置き換えます。

    public class HelloJava {
        public static void main(String[] args) {
            Character myChar = null;
    
            // This line will cause a NullPointerException if myChar is null
            // System.out.println("Is myChar a letter? " + Character.isLetter(myChar));
        }
    }
    

    このコードでは、以下のことを行っています。

    • myChar という名前の Character 変数を宣言し、明示的に null に設定しています。
    • コメントアウトされた行 System.out.println("Is myChar a letter? " + Character.isLetter(myChar)); は、null の引数で静的メソッド Character.isLetter() を呼び出そうとしています。Character.isLetter() は静的メソッドですが、null の Character オブジェクトを渡すと、メソッド内部で Character オブジェクトをプリミティブな char 値にアンボックスしようとするため、依然として NullPointerException が発生します。
  3. ファイルを保存します(Ctrl+S または Cmd+S)。

  4. 次に、プログラムをコンパイルしましょう。WebIDE の下部にあるターミナルを開き、以下のコマンドを実行します。

    javac HelloJava.java
    

    コンパイルが成功すると、何も出力されないはずです。

  5. 次に、プログラムを実行してみましょう。ターミナルで以下のコマンドを実行します。

    java HelloJava
    

    エラーを引き起こす行がコメントアウトされているため、プログラムは何も出力せず、エラーもなく実行されます。これは、単に Character を null として宣言するだけでは即座に問題が発生しないことを示しています。問題は、それに対して操作を行おうとするときに発生します。

次のステップでは、問題のある行のコメントを解除し、NullPointerException を観察します。

null チェックと文字チェックを組み合わせる

前のステップでは、Character オブジェクトを扱う際に NullPointerException が発生する可能性を見ました。今回は、エラーを引き起こす行のコメントを解除して、例外が実際に発生する様子を見てみましょう。その後、null チェックと文字チェックを組み合わせることでこれを防ぐ一般的な方法を学びます。

  1. WebIDE エディタで HelloJava.java ファイルを開きます。

  2. Character.isLetter() を呼び出す行のコメントを解除します。コードは次のようになるはずです。

    public class HelloJava {
        public static void main(String[] args) {
            Character myChar = null;
    
            // This line will cause a NullPointerException if myChar is null
            System.out.println("Is myChar a letter? " + Character.isLetter(myChar));
        }
    }
    
  3. ファイルを保存します(Ctrl+S または Cmd+S)。

  4. ターミナルで修正したプログラムをコンパイルします。

    javac HelloJava.java
    

    再び、コンパイルが成功すると何も出力されないはずです。

  5. 次に、プログラムを実行します。

    java HelloJava
    

    次のような出力が表示され、NullPointerException が発生していることがわかります。

    Exception in thread "main" java.lang.NullPointerException
        at java.base/java.lang.Character.isLetter(Character.java:xxxx)
        at HelloJava.main(HelloJava.java:x)
    

    このエラーは、myChar が null であり、Character.isLetter() メソッドが null オブジェクトに対して動作できないために発生します。

  6. この NullPointerException を防ぐために、Character.isLetter() を呼び出す 前に myChar が null かどうかをチェックすることができます。これには if 文を使用します。HelloJava.java ファイルを修正してこのチェックを追加します。

    public class HelloJava {
        public static void main(String[] args) {
            Character myChar = null;
    
            if (myChar != null && Character.isLetter(myChar)) {
                System.out.println("myChar is a letter.");
            } else {
                System.out.println("myChar is not a letter or is null.");
            }
        }
    }
    

    この更新されたコードでは、以下のことを行っています。

    • &&(論理 AND 演算子)で結合された 2 つの条件を持つ if 文を使用しています。
    • 最初の条件 myChar != null は、myChar が null ではない かどうかをチェックします。
    • 2 番目の条件 Character.isLetter(myChar) は、myChar が文字かどうかをチェックします。
    • && 演算子は「短絡評価」です。つまり、最初の条件 (myChar != null) が false の場合、2 番目の条件 (Character.isLetter(myChar)) は 評価されません。これにより、myChar が null でない場合にのみ Character.isLetter() を呼び出すため、NullPointerException を防ぐことができます。
  7. ファイルを保存します。

  8. 再度プログラムをコンパイルします。

    javac HelloJava.java
    
  9. プログラムを実行します。

    java HelloJava
    

    今回は、プログラムはエラーなく実行され、次のように出力されます。

    myChar is not a letter or is null.
    

    これは、myChar が null であるため、if 文の最初の条件 (myChar != null) が false となり、else ブロックが実行されるからです。

オブジェクトのメソッドやプロパティにアクセスする前に null をチェックするこのアプローチは、Java で NullPointerException を回避するための基本的なテクニックです。

Null 安全のために Optional を使用する

前のステップでは、オブジェクトを使用する前に明示的に null かどうかをチェックすることで NullPointerException を防ぐ方法を学びました。この方法は有効ですが、時にはコードが null チェックで煩雑になることがあります。Java 8 では、潜在的に null 値を持つオブジェクトをより関数型で表現力豊かな方法で扱うために Optional クラスが導入されました。

Optional は、null ではない値を含む場合と含まない場合があるコンテナオブジェクトです。値が存在する場合、isPresent() は true を返し、get() はその値を返します。値が存在しない場合、オブジェクトは空と見なされ、isPresent() は false を返します。空の Optional に対して get() を呼び出すと、NoSuchElementException がスローされます。

Optional<Character> を使用して、Character が null になる可能性を扱うように、例をリファクタリングしてみましょう。

  1. WebIDE エディタで HelloJava.java ファイルを開きます。

  2. ファイルの内容全体を次のコードに置き換えます。

    import java.util.Optional;
    
    public class HelloJava {
        public static void main(String[] args) {
            Character myChar = null; // Still potentially null
    
            // Create an Optional from the potentially null Character
            Optional<Character> optionalChar = Optional.ofNullable(myChar);
    
            // Use Optional methods to check and process the value
            if (optionalChar.isPresent() && Character.isLetter(optionalChar.get())) {
                 System.out.println("myChar is a letter.");
            } else {
                 System.out.println("myChar is not a letter or is null.");
            }
    
            // Another way using Optional's functional methods (more advanced)
            // optionalChar.filter(Character::isLetter)
            //             .ifPresentOrElse(
            //                 c -> System.out.println("myChar is a letter (using Optional methods)."),
            //                 () -> System.out.println("myChar is not a letter or is null (using Optional methods).")
            //             );
        }
    }
    

    このコードでは、以下のことを行っています。

    • Optional クラスをインポートしています。
    • 依然として myChar を潜在的に null として宣言しています。
    • Optional<Character> optionalChar = Optional.ofNullable(myChar); は Optional オブジェクトを作成します。Optional.ofNullable() は、値が null になる可能性がある場合に使用されます。myChar が null の場合、optionalChar は空の Optional になります。myChar に値がある場合、optionalChar はその値を含みます。
    • その後、optionalChar.isPresent() を使用して、Optional が値を含んでいるかどうかをチェックし、optionalChar.get() で値を取得して Character.isLetter() に渡す前に確認します。これは前の null チェックと似ていますが、Optional API を使用しています。
    • コメントアウトされた部分は、filter や ifPresentOrElse などの関数型メソッドを使用して Optional をより高度に使用する方法を示しています。これにより、特定のシナリオでコードをより簡潔にすることができます。この入門実験ではこの高度な使い方には焦点を当てませんが、知っておくと良いでしょう。
  3. ファイルを保存します。

  4. プログラムをコンパイルします。

    javac HelloJava.java
    
  5. プログラムを実行します。

    java HelloJava
    

    出力は前のステップと同じになるはずです。

    myChar is not a letter or is null.
    

    これは、Optional.ofNullable() と isPresent() を使用することで null のケースを正しく扱えることを確認しています。

次に、myChar を null ではない文字に変更して、プログラムがどのように動作するかを見てみましょう。

  1. HelloJava.java ファイルを修正して、myChar を文字(例えば 'A')に設定します。

    import java.util.Optional;
    
    public class HelloJava {
        public static void main(String[] args) {
            Character myChar = 'A'; // Now myChar has a value
    
            // Create an Optional from the potentially null Character
            Optional<Character> optionalChar = Optional.ofNullable(myChar);
    
            // Use Optional methods to check and process the value
            if (optionalChar.isPresent() && Character.isLetter(optionalChar.get())) {
                 System.out.println("myChar is a letter.");
            } else {
                 System.out.println("myChar is not a letter or is null.");
            }
        }
    }
    
  2. ファイルを保存します。

  3. プログラムをコンパイルします。

    javac HelloJava.java
    
  4. プログラムを実行します。

    java HelloJava
    

    今回は、出力は次のようになるはずです。

    myChar is a letter.
    

    これは、myChar に値がある場合、optionalChar.isPresent() が true を返し、Character.isLetter() のチェックが正しく実行されることを示しています。

Optional を使用することで、コードがより読みやすくなり、値が存在しない可能性があることを明示的に示すことができ、予期しない NullPointerException の可能性を減らすことができます。

まとめ

この実験では、Java の Character ラッパークラスを扱う際に、潜在的な null 値をどのように扱うかを学びました。まず、null の Character オブジェクトに対して Character.isLetter() のような静的メソッドを使用しようとすると、NullPointerException が発生する可能性があることを示しました。これは、Character オブジェクトに対して操作を行う前に明示的に null をチェックし、プログラムのクラッシュを防ぐことの重要性を強調しています。