メインコンテンツへスキップ
  1. Posts/

NullPointerExceptionの原因調査方法 #Java

·1275 文字·3 分
プログラミング Java 初心者向け
maruTA / Takayuki MARUYAMA
著者
maruTA / Takayuki MARUYAMA

Javaを使って開発している人が良く遭遇する例外の一つであるjava.lang.NullPointerException(NPE)。経験が浅い人にとってはどう対処すれば良いか分からないという声をよく聞くので、原因となったnullの探し方を軽く紹介します。

NullPointerExceptionが発生する原因
#

そもそものNullPointerExceptionが発生する原因について軽く説明しておきます。

NullPointerExceptionは、“値がnullであるオブジェクトに対するメソッド呼び出しやフィールドアクセス"を行おうとした際に発生する例外です。
コード上は、“nullに評価されるなにか” + ピリオド(.) + フィールド名 or メソッド呼び出し という形式であれば、NullPointerExceptionの発生箇所だと判断することが可能です。

(他にもthrow null;もNPEを発生させますが、あまり多く見かけないと思うのでこの記事では扱いません。)

Java 17より前のバージョンを使っている場合
#

例えば、次のようなコードがあったとします。

class Value {
    private String field;

    String getField() {
        return field;
    }
}

// どこかのメソッド内
Value v;

if (v.getField().isEmpty()) { // HERE

HEREと書いてあるif文でNullPointerExceptionが発生していれば、vもしくはv.getField()のどちらかがnullと評価されていることが推測できます。

別の例を見てみましょう。

void someMethod(int arg) {
    ...
}

Integer value = null;
someMethod(value);

このコードはsomeMethod(value);でNullPointerExceptionが発生します。確かにvaluenullに評価されるのですが、先程示した形式を満たしていません。

Integerという型は、プリミティブなintのwrapper classです。wrapper classの変数にはプリミティブ型と違ってnullを代入可能ですね。
一方、someMethodの引数はプリミティブなintと定義されていますから、Integerを引数で渡すと、自動アンボクシング(Autounboxing)によってIntegerからintへの変換が行われます。
Integerの自動アンボクシングでは Integer#intValue()が使用される( JLS 5.1.8)ため、
“nullに評価されるオブジェクトに対するメソッド呼び出し"となることでNullPointerExceptionが発生するのです。

Java 17以降を使っている場合
#

JEP 358: Helpful NullPointerExceptionsが導入され、何がnullでどの操作に失敗したのかを例外のメッセージで説明してくれます。

jshell> void print(Object arg) {
   ...>     System.out.println(arg.toString());
   ...> }
|  次を作成しました: メソッド print(Object)

jshell> print(null)
|  例外java.lang.NullPointerException: Cannot invoke "Object.toString()" because "<parameter1>" is null
|        at print (#5:2)
|        at (#6:1)

※正確にはJava 14で導入された機能ですが、業務で非LTSかつメンテナンスが終わっているJava 14,15,16を使っている人はさすがにいないですよね?