この状況を観察するのは私にとって非常に混乱しました。
Integer i = null;
String str = null;
if (i == null) { //Nothing happens
...
}
if (str == null) { //Nothing happens
}
if (i == 0) { //NullPointerException
...
}
if (str == "0") { //Nothing happens
...
}
だから、私はボクシング操作が最初に実行されると思います(つまり、Javaはnull
からint値を抽出しようとします)、比較操作は優先順位が低いため、例外がスローされます。
問題は、なぜJavaでこのように実装されているのかということです。参照を比較するよりもボクシングが優先されるのはなぜですか?または、なぜボクシング前にnull
に対する検証を実装しなかったのですか?
現時点では、NullPointerException
がラップされたプリミティブでスローされ、trueオブジェクトタイプではスローされない場合、一貫性がないように見えます。
キーポイントはこれです:
==
_は、2つの参照型の間で常に参照比較です。Integer
とString
を使用する場合は、代わりにequals
を使用します==
_は、常に数値比較です。null
は常にNullPointerException
をスローしますString
に対する多くの特別な処理がありますが、実際にはプリミティブ型ではありません上記のステートメントは、指定されたvalidJavaコードに当てはまります。この理解により、提示したスニペットに矛盾はありません。
関連するJLSセクションは次のとおりです。
JLS 15.21.3参照平等演算子_
==
_および_!=
_等価演算子のオペランドが両方とも参照型またはnull型のいずれかである場合、操作はオブジェクトの等価です。
これにより、次のことが説明されます。
_Integer i = null;
String str = null;
if (i == null) { // Nothing happens
}
if (str == null) { // Nothing happens
}
if (str == "0") { // Nothing happens
}
_
両方のオペランドは参照型であるため、_==
_は参照の等価比較です。
これにより、次のことも説明されます。
_System.out.println(new Integer(0) == new Integer(0)); // "false"
System.out.println("X" == "x".toUpperCase()); // "false"
_
_==
_が数値的に等しい場合、少なくとも1つのオペランドは数値型でなければなりません:
JLS 15.21.1数値等価演算子_
==
_および_!=
_等号演算子のオペランドがboth数値型の場合、またはone is数値型のもう一方は数値型に変換可能であり、オペランドに対して2進数の昇格が実行されます。オペランドの昇格されたタイプが
int
またはlong
の場合、整数の等価性テストが実行されます。昇格された型が_float or
_ double`の場合、浮動小数点の等価性テストが実行されます。バイナリ数値プロモーションは、値セットの変換とボックス化解除の変換を実行することに注意してください。
これは説明します:
_Integer i = null;
if (i == 0) { //NullPointerException
}
_
Effective Java 2nd Edition、Item 49:プリミティブをボックス化されたプリミティブより優先する)からの抜粋です:
要約すると、選択肢がある場合は、ボックスプリミティブよりもプリミティブを優先して使用してください。プリミティブ型はよりシンプルで高速です。ボックス化されたプリミティブを使用する必要がある場合は、注意してください!オートボクシングは、ボックス化されたプリミティブを使用することによる冗長性を減らしますが、危険性は減らしません。プログラムが2つのボックス化されたプリミティブを_
==
_演算子と比較すると、IDの比較が行われますが、これはほぼ間違いなく目的の比較ではありません。プログラムがボックス化されたプリミティブとボックス化されていないプリミティブを含む混合型の計算を行う場合、ボックス化解除を行い、プログラムがボックス化解除を行う場合、NullPointerException
をスローできます。最後に、プログラムがプリミティブ値をボックス化すると、コストがかかり不必要なオブジェクトが作成される可能性があります。
ボックス化されたプリミティブを使用する以外に選択肢がない場所があります。ジェネリック、ただしそうでない場合は、ボックス化プリミティブを使用する決定が正当化されるかどうかを真剣に検討する必要があります。
Integer
型からint
型に変換します」r
がnull
の場合、アンボックス化変換はNullPointerException
をスローします」==
_および_!=
_==
_および_!=
_int num = Integer.getInteger("123")
throw NullPointerException
? (!!!)String.equals
_対_==
_NPEの例はautoboxingのおかげでこのコードと同等です:
if ( i.intValue( ) == 0 )
したがって、i
がnull
の場合、NPE。
if (i == 0) { //NullPointerException
...
}
私は整数であり、0はintですので、実際に行われることはこのようなものです
i.intValue() == 0
Iがnullであるため、これによりnullPointerが発生します。 Stringにはこの操作はありません。そのため、例外はありません。
Javaのメーカーは==
演算子は、さまざまなタイプのオペランドに直接作用します。この場合、Integer I; int i;
比較 I==i;
は、「I
は値がInteger
であるi
への参照を保持していますか?」という質問をすることができます。これは、I
はnullです。残念ながら、Javaは、異なるタイプのオペランドが等しいかどうかを直接チェックしません。代わりに、言語がいずれかのオペランドのタイプを他のand-ifのタイプに変換できるかどうかをチェックしますこのような振る舞いは、変数x
、y
、およびz
のいくつかの型の組み合わせに対して、可能なことを意味します。持つため x==y
およびy==z
だが x!=z
[例x = 16777216f y = 16777216 z = 16777217]。また、比較I==i
は、「Iをint
に変換し、それが例外をスローしない場合、i
と比較します」と翻訳されます。
i == 0
Javaは自動ボックス化解除と数値比較を試行します(つまり、i
によって参照されるラッパーオブジェクトに格納されている値は値0
? ")。
i
はnull
であるため、ボックス化解除はNullPointerException
をスローします。
推論は次のようになります。
JLS§15.21.1数値等価演算子==および!= の最初の文は次のようになります。
等号演算子のオペランドが両方とも数値型であるか、一方が数値型で、もう一方が数値型に変換可能(§5.1.8)である場合、オペランドに対してバイナリ数値プロモーションが実行されます(§5.6.2)。
明らかにi
は数値型に変換可能であり、0
は数値型であるため、オペランドに対してバイナリ数値の昇格が実行されます。
§5.6.2 Binary Numeric Promotion は(とりわけ)言っています:
オペランドのいずれかが参照型の場合、ボックス化解除変換(§5.1.8)が実行されます。
§5.1.8 Unboxing Conversion は(とりわけ)言います:
rがnullの場合、ボックス化解除変換は
NullPointerException
をスローします
Java オートボクシング機能のためです。コンパイラは、比較の右側でプリミティブ整数を使用していることを検出し、ラッパー整数値をプリミティブint値に展開する必要があります。
それが不可能であるため(列に並べるとヌルです)、NullPointerException
がスローされます。