はじめに
この実験では、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 プログラムを作成します。
WebIDE エディタで
HelloJava.javaファイルが開いていない場合は、開きます。ファイルの内容全体を次のコードに置き換えます。
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が発生します。
ファイルを保存します(Ctrl+S または Cmd+S)。
次に、プログラムをコンパイルしましょう。WebIDE の下部にあるターミナルを開き、以下のコマンドを実行します。
javac HelloJava.javaコンパイルが成功すると、何も出力されないはずです。
次に、プログラムを実行してみましょう。ターミナルで以下のコマンドを実行します。
java HelloJavaエラーを引き起こす行がコメントアウトされているため、プログラムは何も出力せず、エラーもなく実行されます。これは、単に
Characterをnullとして宣言するだけでは即座に問題が発生しないことを示しています。問題は、それに対して操作を行おうとするときに発生します。
次のステップでは、問題のある行のコメントを解除し、NullPointerException を観察します。
null チェックと文字チェックを組み合わせる
前のステップでは、Character オブジェクトを扱う際に NullPointerException が発生する可能性を見ました。今回は、エラーを引き起こす行のコメントを解除して、例外が実際に発生する様子を見てみましょう。その後、null チェックと文字チェックを組み合わせることでこれを防ぐ一般的な方法を学びます。
WebIDE エディタで
HelloJava.javaファイルを開きます。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)); } }ファイルを保存します(Ctrl+S または Cmd+S)。
ターミナルで修正したプログラムをコンパイルします。
javac HelloJava.java再び、コンパイルが成功すると何も出力されないはずです。
次に、プログラムを実行します。
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オブジェクトに対して動作できないために発生します。この
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を防ぐことができます。
ファイルを保存します。
再度プログラムをコンパイルします。
javac HelloJava.javaプログラムを実行します。
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 になる可能性を扱うように、例をリファクタリングしてみましょう。
WebIDE エディタで
HelloJava.javaファイルを開きます。ファイルの内容全体を次のコードに置き換えます。
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チェックと似ていますが、OptionalAPI を使用しています。 - コメントアウトされた部分は、
filterやifPresentOrElseなどの関数型メソッドを使用してOptionalをより高度に使用する方法を示しています。これにより、特定のシナリオでコードをより簡潔にすることができます。この入門実験ではこの高度な使い方には焦点を当てませんが、知っておくと良いでしょう。
ファイルを保存します。
プログラムをコンパイルします。
javac HelloJava.javaプログラムを実行します。
java HelloJava出力は前のステップと同じになるはずです。
myChar is not a letter or is null.これは、
Optional.ofNullable()とisPresent()を使用することでnullのケースを正しく扱えることを確認しています。
次に、myChar を null ではない文字に変更して、プログラムがどのように動作するかを見てみましょう。
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."); } } }ファイルを保存します。
プログラムをコンパイルします。
javac HelloJava.javaプログラムを実行します。
java HelloJava今回は、出力は次のようになるはずです。
myChar is a letter.これは、
myCharに値がある場合、optionalChar.isPresent()がtrueを返し、Character.isLetter()のチェックが正しく実行されることを示しています。
Optional を使用することで、コードがより読みやすくなり、値が存在しない可能性があることを明示的に示すことができ、予期しない NullPointerException の可能性を減らすことができます。
まとめ
この実験では、Java の Character ラッパークラスを扱う際に、潜在的な null 値をどのように扱うかを学びました。まず、null の Character オブジェクトに対して Character.isLetter() のような静的メソッドを使用しようとすると、NullPointerException が発生する可能性があることを示しました。これは、Character オブジェクトに対して操作を行う前に明示的に null をチェックし、プログラムのクラッシュを防ぐことの重要性を強調しています。



