NullPointerExceptionとは?
NullPointerExceptionとは、参照型変数にnull値が格納されている時に、参照型変数を参照しようとした場合に発生する例外です。
言葉で説明されても理解しづらいと思うので、以下のサンプルコードでNullPointerExceptionを確認してみましょう。
public class NullPointerExceptionSample { public static void main(String[] args) { StringLength("ポテパン"); StringLength(null); } static void StringLength(String str) { int strlen = str.length(); System.out.println(str + "は" + strlen + "文字です"); } }
上記のコードは、StringLength(String str)の引数に文字列を渡すと、文字列の文字数を出力します。
最初のStringLength(“ポテパン”)では、「ポテパン」という文字列を引数に渡しました。
実行結果を見てみると「ポテパンは4文字です」と出力されるはずです。
一方、StringLength(null);では、null値を引数に渡しています。
実行してみると、こちらはコンパイルエラーになっているのがわかります。これがNullPointerExceptionです。
実行結果↓
ポテパンは4文字です Exception in thread "main" java.lang.NullPointerException at NullPointerExceptionSample.StringLength(NullPointerExceptionSample.java:8) at NullPointerExceptionSample.main(NullPointerExceptionSample.java:4)
NullPointerExceptionの回避方法は?
NullPointerExceptionが発生しないようにするには、どうすればよいのでしょうか?
ここでは、NullPointerExceptionの回避方法をサンプルコード付きで紹介します!
NullPointerExceptionの回避方法は、基本的に参照型変数がnull値かどうかを判定すれば回避可能です。
しかし、中には間違った方法で回避をしてしまう場合もあります。
そのため、「間違った対策方法」と「正しい対策方法」を見ながら回避方法を学びましょう。
NullPointerExceptionの間違った対策方法
NullPointerExceptionの間違った対策方法として、以下の4つを紹介します。
- 例外の握りつぶし
- エラーの握りつぶし
- 例外の上書きスロー
- 例外の再スロー
ひとつずつ解説しますね。
間違った対策方法① 例外の握りつぶし
「例外の握りつぶし」とは、例外をcatchするがその中では何も処理しないことを言います。
サンプルコードを見てみましょう。
public class NullPointerExceptionSample { public static void main(String[] args) { StringLength(null); } static void StringLength(String str) { try{ int strlen = str.length(); System.out.println(str + "は" + strlen + "文字です"); } catch(Exception e){ } } }
上記のコードで例外は発生しなくなりますが、だからと言って仕様通りに動くとは限りません。
例外が起こるのが「本当にエラーなのか」「設計者の意図に沿ったものなのか」が判断できないからです。
そのため、エラーが発生した時にプログラムの修正が難しくなってしまい、例外の握りつぶしは「やってはいけないご法度」とされています。
間違った対策方法② エラーの握りつぶし
例外の握りつぶしと似たようなやり方に「エラーの握りつぶし」もあります。
サンプルコードを見てみましょう。
public class NullPointerExceptionSample { public static void main(String[] args) { StringLength(null); } static void StringLength(String str) { if(str != null){ int strlen = str.length(); System.out.println(str + "は" + strlen + "文字です"); } } }
try-catch文の代わりに、if文でnullチェックをしています。
しかし、例外の握りつぶしと同じく、本当のエラーか意図通りのものなのかが検知できない問題が解決できていません。
また、そもそもstrにnullが渡されること自体がおかしいため、nullが引き渡された場合の処理を入れないことも問題と言えます。
間違った対策方法③ 例外の上書きスロー(throw)
例外の上書きスローとは、本来スローされた例外が上書きされ、隠されてしまうことを言います。
サンプルコードを見てみましょう。
public class NullPointerExceptionSample { public static void main(String[] args) { StringLength(null); } static void StringLength(String str) { try{ int strlen = str.length(); System.out.println(str + "は" + strlen + "文字です"); } catch(Exception e){ throw new IllegalArgumentException("main メソッドで例外が発生しました"); } } }
上記のサンプルコードでは、catch文で例外をキャッチし、新しい例外であるIllegalArgumentExceptionをスローしています。
これは、元の例外を握りつぶしているため記述すべきでないコードなのです。
間違った対策方法④ 例外の再スロー
例外の再スローもNullPointerExceptionの間違った対策方法になります。
サンプルコードを見てみましょう。
public class NullPointerExceptionSample { public static void main(String[] args) { StringLength(null); } static void StringLength(String str) { try{ int strlen = str.length(); System.out.println(str + "は" + strlen + "文字です"); } catch(Exception e){ throw e; } } }
上記のサンプルコードではcatch文で例外をキャッチし、同じ例外をスローしています。
これは、スタックトレースが汚くなってしまうため例外の発生源を見つけるのが困難になります。
NullPointerExceptionの正しい対策方法
次に、NullPointerExceptionの正しい対策方法として、以下の4つを紹介します。
- 変数をnull以外で初期化する
- 配列は空で初期化する
- 戻り値にnullを返さない
- Optionalを使用する
正しい対策方法① 変数をnull以外で初期化する
以下のソースコードでは、「String str = “ポテパン”;」でstrをポテパンという文字列で初期化しています。
初期値にnullを設定しないことで、NullPointerExceptionを回避しているのです。
とてもシンプルな回避方法ですが、無駄なエラーを発生させないための予防策になります。
public class NullPointerExceptionSample { public static void main(String[] args) { String str = "ポテパン"; StringLength(str); } static void StringLength(String str) { int strlen = str.length(); System.out.println(str + "は" + strlen + "文字です"); } }
正しい対策方法② 配列は空で初期化する
配列の初期化をする際に、nullで初期化をしている人もいるのではないでしょうか?
配列の場合は、下記のサンプルコードのように要素が 0 個の配列で初期化すると、NullPointerExceptionの回避になります。
public class NullPointerExceptionSample { public static void main(String[] args) { String[] strarr = new String[] {}; StringLength(strarr); } static void StringLength(String[] str) { System.out.println("配列の要素数は" + str.length + "です"); } }
正しい対策方法③ 戻り値にnullを返さない
配列やメソッドで何も返すべきものがない場合、nullを戻り値として返していませんか?
nullを戻り値で返すと、呼び出し元のほうでnullチェックをする必要が生じ、nullチェックを忘れた場合はNullPointerExceptionが発生します。
戻り値もnullではなく空で返すのがよいでしょう。
正しい対策方法④ Optionalクラスを使用する
Optionalクラスは、戻り値がnullかどうかの判定を自動で行ってくれるクラスです。
このOptionalクラスを使用すると、nullチェック忘れによるエラーやバグを未然に防ぐ役割を果たしてくれます。
詳しいOptionalクラスの使い方は、以下の記事にまとめていますので参考にしてください。
【関連記事】
▶︎【Java】nullチェック忘れを未然に防ぐOptionalクラスについてわかりやすく解説します。
Javaで発生する主な例外、エラーの種類
ここまで読んできた人の中には、「NullPointerException以外にもエラーってあるの?」と思っている方もいるのではないでしょうか?
エラーやバグはプログラムを作成している間に予期せず発生することが多いものです。
今回はたまたまNullPointerExceptionが発生したが、それ以外のエラーやバグが発生した際の対処法も知っておいて損はありません。
Javaで発生する主な例外、エラーの種類については以下の記事で紹介していますので、例外が発生した場合は参考にしてみてください。
【関連記事】
▶︎【Java】Exception(例外)の種類について詳しく解説!
まとめ
NullPointerExceptionの意味や回避方法について解説しました。
NullPointerExceptionはただ回避しようとすると、例外の握りつぶしや上書きスローを発生させてしまいます。
ぜひ、この記事で間違った対策方法と正しい対策方法を比べて、例外処理の対策方法を学んでくださいね。
スタックトレースとは、エラーが発生したときに表示されるものです。
発生したエラーが発生するまでの過程を表示します。