왜 이런 일이 발생합니까? NullPointerException
public static void main(String[] args) throws Exception {
Boolean b = true ? returnsNull() : false; // NPE on this line.
System.out.println(b);
}
public static Boolean returnsNull() {
return null;
}
이 동안하지 않습니다
public static void main(String[] args) throws Exception {
Boolean b = true ? null : false;
System.out.println(b); // null
}
?
용액을 대체하는 방식으로 인 false
으로 Boolean.FALSE
방지하기 null
에 박싱되는 boolean
불가능 실현합니다. 그러나 그것은 질문이 아닙니다. 문제는 왜 ? JLS에이 동작, 특히 두 번째 경우를 확인하는 참조가 있습니까?
답변
차이점은 명시 적 유형의 returnsNull()
메소드가 컴파일시 표현식의 정적 타이핑에 영향을 준다는 것입니다.
E1: `true ? returnsNull() : false` - boolean (auto-unboxing 2nd operand to boolean)
E2: `true ? null : false` - Boolean (autoboxing of 3rd operand to Boolean)
Java 언어 사양, 15.25 절을 참조하십시오. 조건부 연산자? :
-
E1의 경우, 두 번째 및 세 번째 피연산자의 유형은 각각
Boolean
및boolean
각각 이므로이 절이 적용됩니다.두 번째 및 세 번째 피연산자 중 하나가 부울 유형이고 다른 유형의 부울이 부울 유형 인 경우 조건식의 유형은 부울입니다.
식의 유형이이므로
boolean
두 번째 피연산자는로 강제 변환되어야합니다boolean
. 컴파일러는 자동 unboxing 코드를 두 번째 피연산자 (return value ofreturnsNull()
)에 삽입하여 type을 만듭니다boolean
. 이것은 물론 NPE를null
런타임에 반환합니다. -
E2의 경우, 두 번째 및 세 번째 피연산자의 유형은 각각 ( E1과
<special null type>
같지 않음Boolean
),boolean
특정 타이핑 절이 적용되지 않으며 ( ’em!로 이동 ), 마지막 “otherwise”절이 적용됩니다.그렇지 않으면, 두 번째 및 세 번째 피연산자는 각각 S1 및 S2 유형입니다. T1을 복싱 변환을 S1에 적용한 결과 유형으로, T2를 복싱 변환을 S2에 적용한 결과 유형으로 설정합니다. 조건식의 유형은 캡처 변환 (§5.1.10)을 lub (T1, T2) (§15.12.2.7)에 적용한 결과입니다.
- S1 ==
<special null type>
( §4.1 참조 ) - S2 ==
boolean
- T1 == box (S1) ==
<special null type>
( §5.1.7 의 권투 변환 목록에서 마지막 항목 참조 ) - T2 == 상자 (S2) ==`부울
- 윤활유 (T1, T2) ==
Boolean
따라서 조건식의 유형은
Boolean
세 번째 피연산자로 강제 변환되어야합니다Boolean
. 컴파일러는 3 번째 피연산자 (false
)에 대한 자동 복싱 코드를 삽입합니다 . 두 번째 피연산자는에서E1
와 같이 자동 Unboxing이 필요하지 않으므로null
반환 될 때 자동 Unboxing NPE가 없습니다 . - S1 ==
이 질문에는 비슷한 유형 분석이 필요합니다.
답변
라인 :
Boolean b = true ? returnsNull() : false;
내부적으로 다음과 같이 변환됩니다.
Boolean b = true ? returnsNull().booleanValue() : false;
언 박싱을 수행하기 위해; 따라서 : null.booleanValue()
NPE를 산출합니다
이것은 오토 박싱을 사용할 때의 주요 함정 중 하나입니다. 이 동작은 실제로 5.1.8 JLS에 문서화되어 있습니다.
편집 : unboxing은 (암시 적 캐스트 추가)와 같이 세 번째 연산자가 부울 유형이기 때문에 믿습니다.
Boolean b = (Boolean) true ? true : false;
답변
에서 Java 언어 사양, 섹션 15.25 :
- 두 번째 및 세 번째 피연산자 중 하나가 부울 유형이고 다른 유형의 부울이 부울 유형 인 경우 조건식의 유형은 부울입니다.
그래서 첫 번째 예제의 시도는 호출 Boolean.booleanValue()
로 변환하기 위해 Boolean
에 boolean
첫 번째 규칙에 따라.
두 번째 경우 첫 번째 피연산자는 null 유형이고 두 번째 피연산자는 참조 유형이 아닌 경우 오토 박싱 변환이 적용됩니다.
- 그렇지 않으면, 두 번째 및 세 번째 피연산자는 각각 S1 및 S2 유형입니다. T1을 복싱 변환을 S1에 적용한 결과 유형으로, T2를 복싱 변환을 S2에 적용한 결과 유형으로 설정합니다. 조건식의 유형은 캡처 변환 (§5.1.10)을 lub (T1, T2) (§15.12.2.7)에 적용한 결과입니다.
답변
바이트 코드에서이 문제를 볼 수 있습니다. main 바이트 코드의 3 번째 줄 3: invokevirtual #3 // Method java/lang/Boolean.booleanValue:()Z
에서 boxing Boolean of null 값인 invokevirtual
메소드 java.lang.Boolean.booleanValue
는 NPE를 발생시킵니다.
public static void main(java.lang.String[]) throws java.lang.Exception;
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: invokestatic #2 // Method returnsNull:()Ljava/lang/Boolean;
3: invokevirtual #3 // Method java/lang/Boolean.booleanValue:()Z
6: invokestatic #4 // Method java/lang/Boolean.valueOf:(Z)Ljava/lang/Boolean;
9: astore_1
10: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
13: aload_1
14: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
17: return
LineNumberTable:
line 3: 0
line 4: 10
line 5: 17
Exceptions:
throws java.lang.Exception
public static java.lang.Boolean returnsNull();
descriptor: ()Ljava/lang/Boolean;
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: aconst_null
1: areturn
LineNumberTable:
line 8: 0