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


81

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

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래핑 된 프리미티브와 함께 던져지고 진정한 객체 유형으로 던져지지 않을 때 일관성 이 없어 보입니다 .


str.equals ( "0")를 수행하면 NullPointerException이 발생합니다.
Ash Burlaczenko

== 연산자는 어떤 상황에서도 NPE에 대해 저장하는 데 사용되었습니다. 저에게 이것은 Java에서 자동 복싱을 도입하는 것이 얼마나 나쁜 생각인지 보여주는 또 다른 예입니다. 여러 가지 이유로 적합하지 않으며 이전에 없었던 것을 제공하지 않습니다. 실제로 진행되는 작업을 모호하게하는 동안 코드를 더 짧게 만듭니다.
x4u

내 생각은 180도 다릅니다. 그들은 모든 곳에서 객체를 사용하는 프리미티브를 포함해서는 안됩니다. 그런 다음 컴파일러가 프리미티브를 최적화하고 사용하도록합니다. 그러면 혼란이 없을 것입니다.
MrJacqes

답변:


137

짧은 답변

요점은 다음과 같습니다.

  • == 두 참조 유형 간에는 항상 참조 비교입니다.
    • 자주 사용하지 않는 경우 (예 : 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를 사용하기로 한 결정이 정당한지 진지하게 고려해야합니다.

참고 문헌

관련 질문

관련 질문


2
받는 사람으로 이유를 someRef == 0 두 박스 프리미티브의 참조를 비교하는 것은 거의이기 때문에 그것은 매우 소리 선택입니다, 항상 숫자 비교입니다 항상 프로그래머 오류입니다. 이 경우 기본적으로 비교를 참조하는 것은 쓸모가 없습니다.
Mark Peters

2
컴파일러 가이 상용구 널 검사 코드를 작성하기 위해 개발자에게 의존하는 대신 표현식을 (myInteger == 0)로 대체하지 않는 이유는 무엇 (myInteger != null && myInteger == 0)입니까? IMO는 확인할 수 있어야 하며 기본 값이 구체적 일 경우에만 if (myBoolean)평가 해야합니다. 먼저 null 검사를 수행 할 필요가 없습니다. truetrue
Josh M.


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

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

i.intValue() == 0

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


4

자바의 업체가 정의한 수있는 ==경우가 부여하는 직접 다른 유형의 피연산자에 따라 행동하는 연산자, 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.


+1 : 실제로 OP의 질문 인 "왜 그렇게 설계 되었나요?"
Martijn Courteaux 2013

1
@MartijnCourteaux : 많은 언어는 일치하는 유형의 피연산자에 대해서만 연산자를 정의하는 것처럼 보이며 T가 암시 적으로 U로 변환 될 수 있다면 U는 허용 될 수 있지만 T는 허용되지 않을 때마다 불평없이 이러한 암시 적 변환을 수행해야한다고 가정합니다. 그렇지 같은 행동, 언어는 정의 할 수 있었 ==경우 모든 경우에 것과 같은 방식으로 x==y, y==z그리고 x==z불평없이 모든 컴파일, 세 가지 비교가 동치 관계로 작동합니다. 디자이너가 모든 종류의 멋진 언어 기능을 추진하지만 공리적 준수를 무시하는 것이 궁금합니다.
supercat 2013

1

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

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


1

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


0

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

public static Integer getNotNullIntValue(Integer value)
{
    if(value!=null)
    {
        return value;
    }
    return 0;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.