1
1

NullPointerExceptionとprintメソッドの関係について

Posted at

皆さんこんばんは、駆け出しエンジニアの脆弱性アザラシです。

今回は、Javaの資格であるJava SE11 Silverの試験対策をしていて、対策本の一つである『徹底攻略Java SE 11 Silver問題集』通称「黒本」を読んでいて、NullPointerExceptionが発生するタイミングが気になった問題があり、調べてみました。

デフォルト値によるNullPointerException

今回気になったのは、第5章「配列の操作」の問5と問6の問題です。問5ではまず、下記の問題が挙げられています。

次のプログラムをコンパイル、実行したときの結果として、正しいものを選びなさい。(1つ選択)

public class Item {
    String name;
    int price = 100;
}
public class Main {
    public static void main(String[] args){
    Item[] items = new Item[3];
    int total = 0;
    for (int i = 0; i < items.length; i++) {
        total += items[i].price;
        }
        System.out.println(total);
    }
 }

志賀澄人、株式会社ソキウス・ジャパン『徹底攻略Java SE 11 Silver問題集』p139より.

この問題の答えは、実行時に例外NullPointerExceptionがスローされる、です。

このコードでなぜ例外が発生するかというと、Mainクラスの3行目では、「3つのアイテムを扱う配列インスタンス」をitemsで宣言するということしかしておらず、Itemのインスタンスを生成しているわけではないからです。ここではオブジェクト型配列のデフォルト値である「null」で初期化されています(1)。

そのため、Mainクラスの6行目でpriceフィールドにアクセスする段階で、変数の参照先が存在しないことを意味するNullPointerExceptionが発生します。

printメソッドの仕様

気になったのが、この次の問題である問6です。

次のプログラムをコンパイル、実行したときの結果として、正しいものを選びなさい。(1つ選択)

public class Main{
    public static void main(String[]args){
        String[]array = {"A","B","C","D"};
        array[0] = null;
        for(String str : array){
            System.out.print(str);
        }
    }
}

志賀澄人、株式会社ソキウス・ジャパン『徹底攻略Java SE 11 Silver問題集』p140より.

この問題では、Mainクラスの4行目でarray[0] = null;と、要素の値を変更しています。答えは、「nullBCD」と表示されるということなのですが、先ほどの問5を経た私は、「なぜここではNullPointerExceptionが発生しないんだ…?」と疑問に思いました。

要素の値が変更されているのであれば、配列の一つ目に入っているのは「null」で、そのnullを呼び出そうとしているのだから、NullPointerExceptionが発生するはずでは?と思ったのです。

気になったので、レファレンスを参照してみます。すると、NullPointerExceptionが発生するタイミングは、主に次の5種類であると書かれています。

・nullオブジェクトのインスタンス・メソッドの呼出し。
・nullオブジェクトのフィールドに対するアクセスまたは変更。
・nullの長さを配列であるかのように取得。
・nullのスロットを配列であるかのようにアクセスまたは修正。
・nullをThrowable値であるかのようにスロー(2)。

元々レファレンスにおけるNullPointerExceptionの内容を把握していたわけではありませんが、私が問6で例外がスローされると誤認したのは、一番上の「nullオブジェクトのインスタンス・メソッドの呼出し」だと思います。print()メソッドでnullオブジェクトを呼び出しているんじゃないの?と考えたわけです。

どうにも分からなかったためChatGPTに聞いてみたところ、print()メソッドの仕様が関係している、という回答でした。

黒本の回答の解説(黒本p153)にもある通り、nullはどこも参照していないことを示す「リテラル」として、Javaでは扱われます。

GPTによれば、nullがリテラルであるためSystem.out.println(null) のように null を直接 print() メソッドに渡すことができるということのようです。print()メソッドやprintln()メソッドは、そもそもそういう仕様になっていると……うーむ?

―――

記事を書いている最中に、私と全く同じ疑問を感じて「教えて!goo」にて、で質問をしてらっしゃる方がいるのを発見しました(3)。こちらでもどうやら概ね同じ回答のようでして、そもそもprintメソッドとNullPointerExceptionがそういうものだから、ということのようです。レファレンスでprintメソッドについてさらに調べてみると、このような記述が見つかりました。

public void print(String s)
文字列を出力します。引数がnullの場合は、文字列"null"が出力されます。それ以外の場合、文字列の文字はプラットフォームのデフォルトの文字エンコーディングに従ってバイトに変換され、これらのバイトはwrite(int)メソッドとまったく同じ方法で書き込まれます。
パラメータ:
s - 出力されるString値 (4)

printメソッドはそもそも、nullが来た場合はnullと出力されるように定義されている、だからNullPointerExceptionは発生しないという結論のようです。ちょっと知り切れトンボですが……😅

2024年9月16日.

参考
全体.志賀澄人、株式会社ソキウス・ジャパン『徹底攻略Java SE 11 Silver問題集』2019年,インプレス.
1.やさしいデスマーチ「オブジェクト型配列」、最終閲覧日:2024年9月16日.
2.Java(tm)Platform Standard Edition,"クラスNullPointerException
"
、最終閲覧日:2024年9月16日.
3.教えて!goo「Java配列でNullPointerExceptionになるケースとならないケースの差がわかりません」、最終閲覧日:2024年9月16日.
4.Java(tm)Platform Standard Edition,"クラスPrintStream
"
、最終閲覧日:2024年9月16日.

1
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1