public class Npe {
static class Thing {
long value;
}
public static Map<Thing, Long> map;
public static void main(String[] args) {
Thing thing = new Thing();
method(null); // returns -1
method(thing); // returns 0
map = new HashMap<Thing, Long>();
method(null); // returns -1
method(thing); // NullPointerException thrown inside this method call
}
public static long method(Thing thing) {
if (thing == null) {
return -1;
}
Long v = (map == null) ? thing.value : map.get(thing); // NPE here
if (v == null) {
v = thing.value;
}
return v;
}
}
の4回目の呼び出しで、内の指定された行に がスローされますmethod()
。その行をNullPointerException
method()
Long v = (map == null) ? thing.value : map.get(thing);
に
Long v;
if (map == null) {
v = thing.value;
} else {
v = map.get(thing);
}
結果は「いいえ」NullPointerException
で、メソッドは期待どおりに動作します。問題は、「なぜ?」です。
?
コンパイラは、演算子の結果が であると想定して、 への呼び出しの結果( を返して をスローする可能性がある)long
を自動的にアンボックス化( から に降格)してLong
いるように見えます。私見では、演算子の結果が であると想定して、代わりにオートボックス化(に昇格)するべきです。long
map.get(thing)
null
NullPointerException
?
Long
long
Long
thing.value
さらに良いのは、次のステートメントをリファクタリングすることです。
Long v = (map == null) ? thing.value : map.get(thing);
これに(明示的long
にキャストLong
):
Long v = (map == null) ? (Long)thing.value : map.get(thing);
私のIDE(IntelliJ)はキャストが冗長であると表示しますが、コンパイルされたコードは期待どおりに動作し、NullPointerException
!をスローしません。:-D
ベストアンサー1
条件式を検討してください:
(map == null) ? thing.value : map.get(thing)
long
の型thing.value
が であるため、その式の結果は になりますlong
。JLS §15.25 - 条件演算子JLS 8 の表は素晴らしい追加機能です。さまざまな入力タイプに対して可能なすべての出力タイプを明確にします。条件式のタイプに関連する混乱が非常に多かったのです。
このメソッドを次のように呼び出すと、
method(thing);
はmap
ではないので、式のnull
条件は と評価され、次に と評価されて結果が取得されます。map == null
false
map.get(thing)
にはまだエントリがないのでmap
、map.get(thing)
は を返しますnull
。しかし、結果の型は なのでlong
、 に対してアンボックス化操作が実行されnull
、が返されますNPE
。
thing.value
ここで、 を明示的に にキャストするとLong
、式の型は になりますLong
。したがって、 の結果に対してアンボックス化は実行されずmap.get(thing)
、null
に割り当てられますLong v
。