JavaでNullPointerExceptionを防ぐ方法
Lucien Chemaly
2023年9月21日
0 分で読めますJava開発の経験が豊富な方なら、Javaの例外の複雑さについてよくご存じでしょう。例外はプログラムの実行中に発生する異常な状態を示すもので、適切に処理しないとアプリケーションの通常のフローが妨げられ、クラッシュや予期しない動作につながる可能性があります。
さまざまなJava例外の中でも、NullPointerExceptionsには特に注意が必要です。実行時エラーのよくある原因として知られており、アプリケーションの安定性や信頼性に大きな影響を及ぼす可能性があります。
この記事では、JavaのNullPointerExceptionsについて、原因や影響、そして最も重要な予防策を詳しく解説します。
NullPointerExceptionsを避けるべき理由
NullPointerExceptionは、null値を持つオブジェクト参照にアクセスしたり、変更したりしようとしたときにJavaで発生する実行時の異常です。このnull参照は、対象のオブジェクトが存在しない、またはインスタンス化されていないことを示します。Java仮想マシン(JVM)がオブジェクトの操作中にnull参照を検出すると、NullPointerExceptionを生成します。これらの例外が発生する原因を理解し、対処することで、より堅牢で信頼性の高いアプリケーションを開発できます。
NullPointerExceptionsは、次のようなさまざまな状況で発生します。
nullオブジェクトのインスタンス変数にアクセスしたり、そのオブジェクトのメソッドを呼び出したりしたとき。null参照を使って配列やコレクションの要素にアクセスしたとき。ラッパーオブジェクトの
null参照を、対応するプリミティブ型にアンボクシングしたとき。null参照を、nullを想定していないメソッドに引数として渡した場合。
こうした状況は通常、オブジェクトの初期化漏れ、null値の誤った処理、オブジェクトに対する操作の前にnull値を適切に確認していないといったプログラミング上のミスによって発生します。
残念ながら、開発環境ではNullPointerExceptionsによって、次のようなさまざまな悪影響が生じる可能性があります。
アプリケーションのクラッシュ:
NullPointerExceptionが発生すると、多くの場合、アプリケーションが突然終了し、ユーザーの不満や未保存データの損失につながります。予測不能な動作:
NullPointerExceptionsにより、アプリケーションの動作が予測しにくくなり、問題の再現や診断が困難になることがあります。デバッグの難しさ:
NullPointerExceptionの根本原因を特定するには時間がかかり、特に大規模で複雑なコードベースでは困難です。セキュリティ上の脆弱性: 場合によっては、
NullPointerExceptionsがセキュリティ上の脆弱性を引き起こす可能性があります。悪意あるユーザーに悪用され、特定のチェックを回避されたり、機密データへの不正アクセスを許したりするおそれがあります。開発者の生産性低下:
NullPointerExceptionが頻発すると、開発者が修正やテストに余分な時間を費やすことになり、生産性が損なわれる可能性があります。
NullPointerExceptionsの原因や影響、予防策を理解すれば、発生を最小限に抑え、より安定性、効率性、安全性に優れたJavaアプリケーションを開発できます。
JavaでNullPointerExceptionsを防ぐ方法
JavaでNullPointerExceptionsを防ぐために、開発者は三項演算子やApache Commons StringUtilsライブラリの利用など、さまざまな手法やベストプラクティスを活用できます。
三項演算子を使う
if-else文の代わりに三項演算子を使ってnull値を確認し、デフォルト値を設定すれば、NullPointerExceptionsのリスクを軽減できます。三項演算子は、null値を処理してデフォルト値を設定する条件文を簡潔かつ読みやすく記述するのに適しています。
このコードでは、変数inputがnullの場合、変数resultにdefaultを代入します。nullでなければ、inputの値をresultに代入します。三項演算子を使ってnull値を確認し、デフォルト値を設定しています。
Apache CommonsのStringUtilsを使う
Apache Commons StringUtilsライブラリには、文字列内のnull値を安全に処理するユーティリティメソッドが用意されており、NullPointerExceptionのリスクを軽減できます。これにより、null値に起因する予期しないアプリケーションのクラッシュやセキュリティ上の脆弱性を防げます。
このコードでは、org.apache.commons.lang3パッケージのStringUtilsクラスを使い、文字列がnullか、空か、空白文字だけで構成されているかを確認し、その結果をBoolean型の変数isBlankに代入します。
Apache Commons StringUtilsのようなサードパーティーライブラリに起因するセキュリティリスクは、SnykでJavaの脆弱性をスキャンして最小限に抑えられます。また、セキュリティと安定性を確保するには、サードパーティーライブラリを常に最新バージョンに保つことが重要です。Apache Commons StringUtilsの最新バージョンを公式サイトで確認し、プロジェクトのpom.xmlを適宜更新してください。
オブジェクトよりプリミティブ型を使う
可能な限り、対応するラッパークラスではなくプリミティブ型を使いましょう。プリミティブ型はnullにならないため、null値の発生を避けられます。一方、対応するラッパークラスはnullになり得るため、適切に処理しないとNullPointerExceptionが発生する可能性があります。
このコードでは、値が0の整数変数primitiveIntを宣言しています。プリミティブ型なのでnullにはなりません。また、Integerクラスの変数objectIntも宣言していますが、こちらはnullになり得るため、適切に処理しないとNullPointerExceptionにつながる可能性があります。
メソッドからnullを返さない
メソッドからnull値を返す代わりに、空のオブジェクトを返しましょう。
このコードでは、getStrings()メソッドを定義し、null値を返す代わりにCollections.emptyList()メソッドを使って空の文字列リストを返します。この方法は、NullPointerExceptionsを回避し、戻り値の扱いに一貫性を持たせるため、コードの可読性と保守性が向上します。Javaメソッドからnullを返さないための有効な方法として、Optionalの利用があります。この方法については、後ほど詳しく説明します。
.equals()を安全に使う
.equals()はnullではないことが分かっているオブジェクトに対して使うか、オブジェクトの比較でNullPointerExceptionsを避けるためにObjects.equals()を使いましょう。
このコードでは、2つの文字列変数str1とstr2を宣言しています。nullではないstr1オブジェクトで.equals()メソッドを使い、Objects.equals()でstr1とstr2を安全に比較します。null値を確認することで、オブジェクトの比較時にNullPointerExceptionsが発生する可能性を回避します。
メソッドの引数にnullを渡さない
@NotNullなどのアノテーションを使って、メソッドがnull引数を受け取らないことを示すと、コンパイル時のチェックが可能になります。
Lombokの@NonNullアノテーションを使う
Lombokは、Javaコードを簡潔にする広く使われているライブラリです。@NonNullアノテーションを使うと、パラメーターがnullでないことを保証し、適切なnullチェックを生成できます。
このコードでは、Lombokライブラリから@NonNullアノテーションをインポートし、doSomethingメソッドのinputパラメーターがnullであってはならないことを指定しています。これにより、コンパイル時にnull値をチェックして、NullPointerExceptionsの発生を防ぎます。
nullオブジェクトパターンを使う
nullオブジェクトパターンは、null値の代わりにデフォルトのオブジェクトを用意するデザインパターンで、NullPointerExceptionsのリスクを軽減します。
このコードでは、Animalインターフェースと、それを実装するDogおよびNullAnimalの2つのクラスを定義しています。AnimalFactoryクラスにはString引数を受け取るgetAnimal()メソッドがあり、引数に応じてDogまたはNullAnimalのインスタンスを返します。
この方法では、無効な引数やnull引数がgetAnimal()に渡された場合に、デフォルトのNullAnimalオブジェクトを作成してnull値を処理できます。これにより、NullPointerExceptionsを防ぎ、プログラムの動作に一貫性を持たせることができます。
Java Stream APIでコレクション内のnull値を処理する
Java Stream APIには、コレクション内のnull値を安全に処理するメソッドが用意されています。
このコードでは、ストリームを使って文字列リストからnull要素を除外します。これにより、リスト内のnull値にアクセスしたときに発生する可能性のあるNullPointerExceptionsを防ぎます。
この方法ではnullチェックが簡潔になり、コレクション内のnull値に起因する予期しないアプリケーションのクラッシュやセキュリティ上の脆弱性を防ぐのに役立ちます。
専用のユーティリティメソッドでメソッド引数を検証する
引数を検証するユーティリティメソッドを作成し、null値が見つかった場合にカスタム例外をスローします。メソッドのパラメーターがnullでないことを保証するのに役立ちます。
このコードでは、checkNotNullメソッドを定義しています。このメソッドは、referenceパラメーターがnullの場合にIllegalArgumentExceptionをスローします。また、checkNotNullメソッドを使って、実装を続行する前にinputパラメーターがnullでないことを確認するdoSomethingメソッドも定義しています。
この方法は、実行時にnull値をチェックし、null値が見つかった場合に例外をスローすることで、NullPointerExceptionsを防ぐのに役立ちます。
インターフェースでデフォルトメソッドを使う
インターフェースを使う際は、nullオブジェクトを処理するデフォルトメソッドを用意して、NullPointerExceptionsを防ぎましょう。
このコードでは、デフォルトのprocess()メソッドを持つProcessorインターフェースを定義しています。このメソッドはnull入力をチェックし、doProcess()またはhandleNullInput()を呼び出します。PMyProcessorクラスはProcessorインターフェースを実装し、doProcess()とhandleNullInput()メソッドをオーバーライドして、独自の動作を定義します。
この方法では、null入力を一貫した方法で処理できるため、NullPointerExceptionsの発生を防ぐのに役立ちます。null値が見つかった場合の独自処理や例外処理も実装できます。
Builderパターンを使う
Builderパターンを使うと、任意のパラメーター(色、サイズ、形状など、オブジェクトのカスタマイズ可能な属性)を持つ複雑なオブジェクトを作成できます。必須パラメーターが常に設定され、nullにならないようにするのにも役立ちます。
This code defines a Person class with a private constructor that only accepts a Builder object, which contains two fields: firstName and lastName. The Builder class ensures that the firstName field is not null by using Objects.requireNonNull() to check it at construction time, while the lastName field is allowed to be null but must be checked and set using the lastName() method.
この方法では、構築時に必須フィールドがすべて初期化され、null値でないことを確認することで、NullPointerExceptionsの発生を防ぎます。プログラムの一貫性と信頼性の向上にもつながります。
singletonパターンでダブルチェックロックパターンを使う
singletonsを作成する際は、ダブルチェックロッキングパターンを使うことで、singletonインスタンスが常にnull以外であることを保証できます。
このコードでは、実行時にクラスのインスタンスが1つだけ作成されるようにするSingletonクラスを定義し、ダブルチェックロッキングによって潜在的な競合状態やNullPointerExceptionsを防いでいます。
この方法はNullPointerExceptionsの防止に重要です。Singletonクラスのインスタンスが一度だけ作成され、その後のgetInstance()メソッドの呼び出しでは同じインスタンスが返されるためです。これにより、instance変数に誤ってnull値が代入され、潜在的なNullPointerExceptionsが発生するのを防げます。
Optionalクラスを使う
Optionalクラスは、潜在的なnull値を明示的に処理することでNullPointerExceptionsを回避できる強力なツールです。
このコードではOptionalクラスを使ってnull値を処理し、値が存在しない可能性を明示しています。また、orElse()でデフォルト値を指定することで、潜在的なNullPointerExceptionsを防ぎます。Optionalに対してget()を直接呼び出さないことが重要です。代わりにorElse()などのメソッドを使い、Optionalが空の場合にデフォルト値を指定することで、NoSuchElementExceptionを防ぎます。
この方法を使うと、値が存在する場合と存在しない場合が明確になるだけでなく、それぞれのケースを適切に処理できるため、NullPointerExceptionやNoSuchElementExceptionなどの潜在的なエラーを回避できます。
早い段階から頻繁にテストする
本番環境で問題が発生する前に、開発中にNullPointerExceptionsを検出できるよう、網羅的な単体テストを作成しましょう。
このコードでは、MyClassクラスのgetStrings()メソッドを対象とするJUnitテストケースを定義しています。次にMyClassのインスタンスを作成してgetStrings()メソッドを呼び出し、assertNotNull()メソッドを使って結果がnullではないことを検証します。
この方法では、後続の処理を行う前にgetStrings()メソッドの結果がnot nullであることを検証するため、潜在的なNullPointerExceptionsの防止に役立ちます。結果がnot nullであることを確認することで、テストケースはnull値に関する潜在的な問題を特定し、予期しないアプリケーションのクラッシュやセキュリティ上の脆弱性を防ぐのに役立ちます。
NullPointのまとめ
JavaアプリケーションでNullPointerExceptionsを防ぐことは、堅牢で信頼性が高く、安全なソフトウェアを作成するうえで不可欠です。Java 14以降ではNullPointerExceptionの処理が強化され、こうした例外の防止やトラブルシューティングが容易になっています。そのため、デバッグの観点からも、新しいバージョンのJavaへのアップデートが有効です。
三項演算子の使用、メソッド引数の検証、BuilderパターンやNull Objectパターンなどのデザインパターンの採用といった手法を組み合わせることで、NullPointerExceptionsが発生するリスクを効果的に軽減できます。こうした実践を取り入れることで、アプリケーションの安定性が向上するだけでなく、開発体験全体の改善にもつながります。
Snykは、安全で信頼性の高いアプリケーションの開発を目指す開発者にとって欠かせないツールです。Snykでは、コード内のセキュリティ脆弱性を検出して修正するためのさまざまなツールやサービスを利用できます。また、一般的な開発環境やプラットフォームとのインテグレーションも提供しており、開発プロセスにセキュリティを簡単に組み込めます。
Java開発をさらにレベルアップし、ソフトウェアエコシステムのセキュリティと安定性に貢献するために、SnykのIDEプラグイン、CLI、無料で実践的なJavaセキュリティレッスンなど、Snykの強力なツールの活用をご検討ください。これらのツールを使えば、NullPointerExceptionsを含むセキュリティ脆弱性を検出して報告し、アプリケーションの安全性と信頼性を確保できます。
Javaの脆弱性を検出して修正
Snykの脆弱性スキャンと修正アドバイスで、アプリケーションを保護しましょう。
クレジットカードは不要です。
またはAzure AD Docker ID Bitbucketで登録
Snykを利用することで、利用規約およびプライバシーポリシーを含む各種ポリシーに同意したものとみなされます。



