問題
突然ですが、次のコードはNullPointerException
になる可能性があります。どのような場合でしょうか?
boolean iszero = n == 0;
答え
答えは、変数n
の型がInteger
で、値がnull
の場合にNullPointerException
が発生します。たとえば以下のようなMain.java
があるとします。
Main.java
public class Main {
public static void main(String[] args) throws Exception {
Integer n = null;
boolean iszero = n == 0;
}
}
これをコンパイルし、実行すると、確かにNullPointerException
が発生することが確認できます。
$ javac Main.java
$ java Main
Exception in thread "main" java.lang.NullPointerException
at Main.main(Main.java:4)
解説
Java Language Specification の 4.2.2. Integer Operations には次の通りに記述されています。
Any integer operator can throw a NullPointerException if unboxing conversion (§5.1.8) of a null reference is required.
要するにJava言語の仕様というわけで、Javaプログラマは粛々と従うほかないわけですが、それではさみしいので、一応自分なりの解釈を述べておくと -- 上記のコードの右辺はリテラルで宣言された0、すなわち (2019/11/11 コメントをもとに修正)int
型です。そのため左辺はunboxingされ、Integer
からint
にキャストされます。ですが左辺n
はnull
。int
型はnull
を許容しないので、NullPointerException
になるわけです。
愚痴
-
こういう場合は「(2019/11/11 コメントをもとに修正)null
によるUnboxingの失敗」を示す専用例外を投げてほしいという気持ちもあったりなかったり。NullPointerException
だと「キャストに失敗した」という成分がすっぽり抜けてしまうので(´・ω・`) - Javaプログラマの間ではこの性質はよく知られており、自分も知っていたのですが、それに気づかず、1時間近くを溶かしたというのが、この記事を書いたきっかけになります……。言い訳すると、上記のサンプルコード程度であればすぐ気がついたと思うのですが、実際は以下のようなコードでした。これじゃわかるもんもわからん(´・ω・`)
List<Integer> digits = new ArrayList<>();
//
// すごいスパゲッティコード
//
digits.add(null); // ここでまぎれこんだ
//
// すごいスパゲッティコード
//
for (int i = 0; i < digits.size(); i++) {
//
// すごいスパゲッティコード
//
if (digits.get(i) == 0) { // ここでNullPointerException
//
// すごいスパゲッティコード
//
}
//
// すごいスパゲッティコード
//
}