NullPointerExceptionとは?原因と解決策をご紹介

はじめに

プログラミングにはエラーが付き物です。特に、Javaにおいては実行後エラーになってアプリケーションが強制終了してしまうケースが少なくありません。コンパイルエラーならエディタが間違いを指摘してくれますが、実行時エラーは解決するのが難しく、エンジニアの中には、解決にたくさんの時間を要した方も非常に多いでしょう。

Javaには、たくさんの種類のエラーがあります。しかし、その中でも非常に遭遇しやすいのがNullPointerExceptionです。今回は、NullPointerExceptionの基礎と発生する原因や解決策をご紹介します。

この記事を読むと以下の3つのことがわかります。

  • NullPointerExceptionとは何か?
  • NullPointerExceptionが発生する原因
  • NullPointerExceptionを解決する方法

NullPointerExceptionとは?

NullPointerExceptionとは、RuntimeExceptionを継承したクラスのひとつで、オブジェクトが必要であるのにも関わらずアプリケーションがnullを使用すると発生するエラーです。JDK1.0で導入されたクラスになります。

ちなみに、nullとは空っぽを意味する表現のひとつです。例えば、下記のように変数にnullを代入すると、空っぽの変数であることを意味します。

String str = null;

また、下記のように変数が空っぽかどうか確認するために、if文でも使用されます。

if(str == null){
System.out.println("変数は空っぽです");
}

NullPointerExceptionは、Javaエンジニアなら遭遇しない人はいないと言えるほど頻発するエラーです。学習者には、挫折になりやすいポイントであり、熟練したエンジニアにとっても解決に時間のかかるエラーと言えます。

NullPointerExceptionの原因

NullPointerExceptionは、メモリが割り当てられていない参照を利用したことが原因で発生するエラーです。例えば、nullオブジェクトのメソッド、インスタンスを呼び出した場合やnullオブジェクトのフィールドにアクセスした際などにエラーが発生します。

ここでは、コードを利用して具体的な原因をチェックしていきましょう。まずは下記のコードを見てください。

public class ConcreteExample{
public static void main(String[] args) {
String str = null;
System.out.println("strの文字数" + str.length());
}
}

文字列型変数strの文字数を数えてそれを出力するプログラムです。このコードでは、str.length()の部分でNullPointerExceptionが発生します。なぜなら、strは空っぽで文字を数えることができないからです。

また、新しいクラスのフィールドがnullで、それをlengthメソッドで呼び出した場合も同じエラーが発生します。

public class ConcreteExample2{
public static void main(String[] args) {
try{
StringClassExample s = new StringClassExample();
System.out.println("strの文字数" + s.str.length());
}catch(Exception e){
e.printStackTrace();
}
}
}

public class StringClassExample{
String str;
}

StringClassExampleオブジェクトのstrフィールドにlengthメソッドを呼び出したコードです。strには文字列が代入されておらず、nullになりますのでエラーが発生します。

NullPointerExceptionを解決するための方法

原因の次に解決策を解説します。解決できる方法は4つです。

変数をnull以外で初期化

NullPointerExceptionが発生する理由は、変数等にnullが代入されているからです。逆に言えば、nullを代入しなければ、このエラーは発生しないと言えるでしょう。つまり、null以外で初期化をしてあげれば解決することができます。この解決策はもっとも単純で明快な方法です。

public class ConcreteExample{
  public static void main(String[] args) {
    String str = "名無しさん";
    WordCount(str);
  }
 
  static void WordCount(String str) {
      int strLen = str.length();
      System.out.println(str + "は" + strLen + "文字になります");
  }
}

上記のコードでは、名無しさんという文字列で初期化しているので、エラーが発生することがありません。また、初期化部分を下記のように変更してもエラーが発生しないので、具体的な文字列を入れて初期化したくないという方は参考にしてください。

String str = "";

戻り値をnullにしない

2つ目の解決策は戻り値をnullにしないことです。まずは下記のコードを見てください。

public class ConcreteExample{
  public static void main(String[] args) {
    String str = null;
    WordCount(str);
  }
 
  static void WordCount(String str) {
      int strLen = str.length();
      System.out.println(str + "は" + strLen + "文字になります");
  }
}

strにnullが代用されており、空っぽなのでNullPointerExceptionが発生します。しかし、下記のように変更すると、万が一strが空っぽでもエラーは回避することが可能です。

public class ConcreteExample{
  public static void main(String[] args) {
    String str = null;
    WordCount(str);
  }
 
  static void WordCount(String str) {
      String s;
   if(str == "" || str == "null"){
      s = "名無しさん"
}else{
s = str;
}
      int strLen = s.length();
      System.out.println(s + "は" + strLen + "文字になります");
  }
}

もし、strに文字列が代入されなかったら、名無しさんが変数に代入されてその文字数を出力するため、エラーが回避できるというわけです。

配列は空で初期化

3つ目の方法は配列を0個で初期化することです。Javaエンジニアの中には、配列を下記のようにnullで初期化している方も少なくありません。

String[] str = null;

しかし、nullで初期化すると変数と同様にNullPointerExceptionの原因になります。配列は下記のように空で初期化するのがおすすめです。

String[] str = new String[] {};

0個の配列になりますので、lengthメソッドで呼び出しても何も出力されず、当然エラーも発生しません。もちろん、NullReferenceExceptionも回避することができるので、実行時エラーで悩ませられる回数を減らすことができるでしょう。

Optionalクラスを利用する

Optionalは、変数がnullであるのかどうかを自動的に判定してくれるクラスです。Optionalクラスには、isPresentやifPresentOrElse等のメソッドが準備されており、それらを活用することで、自動でnullかどうかをチェックすることができます。

OptionalクラスのisPresentメソッドを使用すると、値がnullのときはfalseを、値が存在する場合はtrueを返します。ifPresentOrElseは、値が存在するときはConsumerを実行し、存在しない場合はRunnableを実行するメソッドです。ブロック外の変数を書き換えられませんが、活用すると非常に便利でしょう。

NullPointerExceptionの間違った解決策は?

実は、解決方法は複数種類あり、その選択はプログラマーに委ねられます。しかし、間違った解決策を選んでしまうとコードの修正に手間がかかったり、例外が上書きされてしまったりするのです。この見出しでは、間違った解決策を詳しく解説していきます。

エラーの塗りつぶし

エラーの塗りつぶしは間違った解決策です。if文で変数がnullかどうか比較して、nullでない場合は処理を実行する方法のことになります。

public static void Main(string[] args)
        {
            String txt = null;
            output(txt);
        }

        public static void output(string txt)
        {
            if (txt != null)
            {
                System.out.println(txt + "の文字数は" + txt.length()");
            }
        }

上記のコードはコンパイルエラーだけでなく、実行時エラーも発生しません。しかし、txtにnullが代入されること自体、おかしいことです。また、本当のエラーが検出できなくなるため、不適切な解決策になります。

例外の握りつぶし

try…catch文でNullPointerExceptionを回避するのは間違った方法です。

try{
System.out.println(txt + "の文字数は" + txt.length()");
}catch(Exception){
return;
}

NullPointerExceptionが発生しそうな箇所にtry…catch文を使うとtxtがnullであってもキャッチされるのでエラーが発生しなくなります。一見良さそうな解決策ですが、例外の発生が本当のエラーなのか、それとも設計者が意図的に行ったものなのかわからないため不適切です。try…catch文でエラーを回避することを例外の握りつぶしと言い、やっていけない方法のひとつになります。

例外の再スロー

try…catch文で例外をスローすることを例外の再スローと言います。

try{
System.out.println(txt + "の文字数は" + txt.length()");
}catch(Exception){
throw;
}

例外の握りつぶしと同様に、正常に動作するプログラムです。しかし、同じ例外をスローするとスタックトレースが汚れてしまい例外の発生源が見つかりづらくなります。エラーを見つけ出すときはコードを修正する際に手間がかかりますので、避けなければならない解決策です。

まとめ

Javaプログラミングをする際に、NullPointerExceptionは避けることができないエラーのひとつです。そしてコンパイルエラーではないので解決に時間がかかるのも事実でしょう。原因や解決策を知っておくことで、このエラーに遭遇したときに短時間で解決できるようになるのではないでしょうか。

また、単純なコードならば問題ありませんが、プログラムが複雑になればなるほど、ソースコードをどのように書くのかは重要です。間違った回避方法を選択するとプログラムが複雑になったり、修正に時間がかったりするので、正しい方法でNullPointerExceptionを解決してください。




エンジニア募集中

株式会社キャパでは中途エンジニアの募集をしています。
私たちと一緒に働きませんか?
募集概要について詳しくは


◆参考URL

https://docs.oracle.com/javase/jp/7/api/java/lang/NullPointerException.html

http://topickup.web.fc2.com/NullPointerException .html

関連記事一覧