[java] 부울, 조건부 연산자 및 오토 박싱

왜 이런 일이 발생합니까? 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의 경우, 두 번째 및 세 번째 피연산자의 유형은 각각 Booleanboolean각각 이므로이 절이 적용됩니다.

    두 번째 및 세 번째 피연산자 중 하나가 부울 유형이고 다른 유형의 부울이 부울 유형 인 경우 조건식의 유형은 부울입니다.

    식의 유형이이므로 boolean두 번째 피연산자는로 강제 변환되어야합니다 boolean. 컴파일러는 자동 unboxing 코드를 두 번째 피연산자 (return value of returnsNull())에 삽입하여 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가 없습니다 .


이 질문에는 비슷한 유형 분석이 필요합니다.

Java 조건부 연산자? : 결과 유형


답변

라인 :

    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()로 변환하기 위해 Booleanboolean첫 번째 규칙에 따라.

두 번째 경우 첫 번째 피연산자는 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


답변