[java] Integer와 int를 비교하면 Java에서 NullPointerException이 발생할 수있는 이유는 무엇입니까?

이 상황을 관찰하는 것은 매우 혼란 스럽습니다.

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가에서 int 값을 추출하려고 시도 함 null) 비교 작업의 우선 순위가 낮기 때문에 예외가 발생합니다.

문제는 왜 Java에서 이런 방식으로 구현됩니까? 권투가 참조 비교보다 우선 순위가 높은 이유는 무엇입니까? 아니면 왜 null복싱 전에 검증을 구현하지 않았 습니까?

현재는 NullPointerException래핑 된 프리미티브와 함께 던져지고 진정한 객체 유형으로 던져지지 않을 때 일관성 이 없어 보입니다 .



답변

짧은 답변

요점은 다음과 같습니다.

  • == 두 참조 유형 간에는 항상 참조 비교입니다.
    • 자주 사용하지 않는 경우 (예 : IntegerString) equals대신 사용하는 것이 좋습니다.
  • == 참조 유형과 숫자 기본 유형 사이는 항상 숫자 비교입니다.
    • 참조 유형은 unboxing 변환의 대상이됩니다.
    • 개봉기는 null항상 던집니다NullPointerException
  • Java에는에 대한 많은 특수 처리가 있지만 String실제로는 기본 유형이 아닙니다.

위의 문은 주어진 유효한 Java 코드를 유지합니다. 이러한 이해를 바탕으로 귀하가 제시 한 스 니펫에 어떤 불일치도 없습니다.


긴 답변

다음은 관련 JLS 섹션입니다.

JLS 15.21.3 참조 같음 연산자 ==!=

같음 연산자의 피연산자가 모두 참조 유형이거나 유형이면 연산은 객체 같음입니다.

이것은 다음을 설명합니다.

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"

들어 ==숫자 평등으로, 피연산자 중 적어도 하나는 숫자 유형이어야합니다 :

JLS 15.21.1 숫자 평등 연산자 ==!=

항등 연산자의 피연산자가 모두 숫자 유형이거나 하나가 숫자 유형이고 다른 하나가 숫자 유형 으로 변환 가능한 경우 피연산자에 대해 2 진 숫자 승격이 수행됩니다. 승격 된 피연산자의 유형이 int또는 long인 경우 정수 동등성 테스트가 수행됩니다. 승격 된 유형이 float or double`이면 부동 소수점 동등성 테스트가 수행됩니다.

이진 숫자 승격은 값 세트 변환 및 개봉 변환을 수행합니다.

이것은 다음을 설명합니다.

Integer i = null;

if (i == 0) {  //NullPointerException
}

다음은 Effective Java 2nd Edition, 항목 49 에서 발췌 한 것입니다 . 박스형 기본 형식보다 기본 형식을 선호합니다 .

요약하면, 선택할 수있을 때마다 boxed primitive보다 우선적으로 primitive를 사용하십시오. 기본 유형은 더 간단하고 빠릅니다. 박스형 프리미티브를 사용해야한다면 조심하세요! Autoboxing은 boxed primitives를 사용하는 위험은 아니지만 자세한 정도를 줄입니다. 프로그램이 두 개의 박스형 프리미티브를 ==연산자 와 비교할 때 ID 비교를 수행합니다. 이는 거의 확실히 원하는 것이 아닙니다. 프로그램이 boxed 및 unboxed 프리미티브와 관련된 혼합 유형 계산을 수행하면 unboxing을 수행하고 프로그램이 unboxing을 수행하면 NullPointerException. 마지막으로, 프로그램이 기본 값을 상자에 넣으면 비용이 많이 들고 불필요한 객체 생성이 발생할 수 있습니다.

제네릭과 같은 boxed primitive를 사용하는 것 외에 선택의 여지가없는 곳이 있습니다. 그렇지 않으면 boxed primitive를 사용하기로 한 결정이 정당한지 진지하게 고려해야합니다.

참고 문헌

관련 질문

관련 질문


답변

NPE 예제는 autoboxing 덕분에이 코드와 동일합니다 .

if ( i.intValue( ) == 0 )

따라서 NPE inull.


답변

if (i == 0) {  //NullPointerException
   ...
}

나는 정수이고 0은 정수이므로 실제로 수행되는 작업은 다음과 같습니다.

i.intValue() == 0

그리고 i가 null이기 때문에 nullPointer가 발생합니다. String의 경우이 작업이 없으므로 예외가 없습니다.


답변

자바의 업체가 정의한 수있는 ==경우가 부여하는 직접 다른 유형의 피연산자에 따라 행동하는 연산자, Integer I; int i;비교 I==i;질문을 수 “를합니까는 I에 대한 참조를 개최 Integer, 그 값을 i?”- 어려움없이 대답 할 수있는 질문을 Inull 인 경우에도 . 불행히도 Java는 다른 유형의 피연산자가 같은지 직접 확인하지 않습니다. 대신 언어가 두 피연산자의 유형을 다른 유형으로 변환 할 수 있는지 확인하고 변환 된 피연산자를 변환되지 않은 피연산자와 비교합니다. 변수 그러한 행동 수단 x, yz유형의 몇 가지 조합으로, 그것을 가지고하는 것이 가능 x==y하고 y==z있지만,x!=z[예 : x = 16777216f y = 16777216 z = 16777217]. 또한 비교 I==i가 “I를로 변환 int하고 예외가 발생하지 않으면. “로 변환 됨을 의미합니다 i.


답변

Javas autoboxing 기능 때문입니다 . 컴파일러는 비교의 오른쪽에서 기본 정수를 사용하고 있으며 래퍼 정수 값을 기본 정수 값으로 분리해야 함을 감지합니다.

그것은 가능하지 않기 때문에 (줄을 긋은 것처럼 null NullPointerException입니다) 던졌습니다.


답변

i == 0Java 에서는 자동 언 박싱을 시도하고 수치 비교를 수행합니다 (예 : “값 i과 동일하게 참조되는 래퍼 객체에 저장된 값 0입니까?”).

이후 i입니다 null언 박싱이 발생합니다 NullPointerException.

추론은 다음과 같습니다.

JLS § 15.21.1 Numerical Equality Operators == 및! = 의 첫 번째 문장은 다음과 같이 읽습니다.

등호 연산자의 피연산자가 모두 숫자 유형이거나 하나는 숫자 유형이고 다른 하나는 숫자 유형으로 변환 가능한 (§5.1.8) 피연산자에 대해 이진 숫자 승격이 수행됩니다 (§5.6.2).

분명히 i숫자 유형으로 변환 가능하고 숫자 유형 0이므로 이진 숫자 승격이 피연산자에서 수행됩니다.

§ 5.6.2 이진 숫자 프로모션 은 다음과 같이 말합니다.

피연산자 중 하나라도 참조 유형이면 unboxing 변환 (§5.1.8)이 수행됩니다.

§ 5.1.8 Unboxing Conversion 은 다음과 같이 말합니다.

경우 , R은 널 (null)이, 언 박싱 변환은 던졌습니다NullPointerException


답변

단순히 메서드를 작성하고 호출하여 NullPointerException을 피하십시오.

public static Integer getNotNullIntValue(Integer value)
{
    if(value!=null)
    {
        return value;
    }
    return 0;
}