삼항 연산자로 허용되지만 if 문이 아닌 정수로 null을 반환


186

다음 스 니펫에서 간단한 Java 코드를 살펴 보겠습니다.

public class Main {

    private int temp() {
        return true ? null : 0;
        // No compiler error - the compiler allows a return value of null
        // in a method signature that returns an int.
    }

    private int same() {
        if (true) {
            return null;
            // The same is not possible with if,
            // and causes a compile-time error - incompatible types.
        } else {
            return 0;
        }
    }

    public static void main(String[] args) {
        Main m = new Main();
        System.out.println(m.temp());
        System.out.println(m.same());
    }
}

이 가장 간단한 Java 코드 temp()에서 함수의 리턴 유형이이지만 메소드는 컴파일러 오류를 발행하지 않으며 (문을 통해 ) int값을 리턴하려고합니다 . 컴파일되면 런타임 예외가 발생 합니다.nullreturn true ? null : 0;NullPointerException

그러나 if( same()메소드 에서 와 같이) 명령문으로 삼항 연산자를 나타내면 컴파일 타임 오류 발생하는 경우에도 같은 문제가 발생합니다! 왜?


6
또한, int foo = (true ? null : 0)new Integer(null)모두 컴파일 벌금, 오토 박싱의 명시적인 형태 인 두 번째.
이즈 카타

2
@Izkata 여기서 문제는 컴파일러가 왜 오토 박스 null를 만들려고하는지 이해하는 Integer것입니다. "나에게"추측 "하거나"일이 잘되도록하는 것 "처럼 보일 것입니다.
Marsellus Wallace

1
... Humm, Integer 생성자 (내가 찾은 문서가 자동 상자에 사용됨)가 String을 인수로 사용할 수 있기 때문에 대답이 있다고 생각했습니다 (null 일 수 있음). 그러나 그들은 또한 생성자가 parseInt () 메소드와 동일하게 작동한다고 말하는데,이 메소드는 null을 전달하면 NumberFormatException을 발생
시킵니다

3
@Izkata-Integer의 문자열 인수 c'tor는 오토 박스 작업이 아닙니다. 문자열은 정수에 자동 상자로 넣을 수 없습니다. (함수 Integer foo() { return "1"; }는 컴파일되지 않습니다.)
Ted Hopp

5
삼항 연산자에 대해 시원하고 새로운 것을 배웠습니다!
oksayt

답변:


118

컴파일러는 null에 대한 null 참조로 해석 Integer하고 조건부 연산자 ( Java Language Specification, 15.25에 설명 된대로)에 대한 오토 박싱 / 언 박싱 규칙을 적용하고 행복하게 진행합니다. NullPointerException런타임시 이를 생성하여 시도하여 확인할 수 있습니다.


게시 한 Java 언어 사양에 대한 링크가 주어지면 위 질문의 경우 어느 시점에서 실행된다고 생각합니까? 마지막 하나 (난 아직도 이해하기 위해 노력하고있어 이후 capture conversionlub(T1,T2)) ?? 또한 실제로 권투를 null 값에 적용 할 수 있습니까? 이것이 "추측"과 같지 않습니까?
Marsellus Wallace

@Gevorg 널 포인터는 가능한 모든 객체에 대한 유효한 포인터이므로 아무 것도 일어날 수 없습니다. 컴파일러는 null이 정수라고 가정하고 int로 autobox 할 수 있습니다.
Voo

1
@Gevorg-nowaq의 의견과 그의 게시물에 대한 나의 답변을 참조하십시오. 나는 그가 올바른 절을 선택했다고 생각합니다. lub(T1,T2)T1 및 T2의 유형 계층에서 공통적으로 가장 구체적인 참조 유형입니다. (둘 다 최소한 Object를 공유하므로 항상 가장 구체적인 참조 유형이 있습니다.)
Ted Hopp

8
@Gevorg- Integer로 박스 화null 되지 않고 Integer에 대한 참조로 해석 됩니다 (null 참조이지만 문제는 아닙니다). null로 구성된 Integer 객체가 없으므로 NumberFormatException의 이유가 없습니다.
Ted Hopp

1
@Gevorg- 복싱 변환 규칙을 보고 이를 null기본 숫자 형이 아닌 규칙에 적용하는 경우 적용 가능한 절은 " p 가 다른 유형의 값인 경우 복싱 변환은 항등 변환과 같습니다. ". 그래서 변환 권투 nullInteger수율을 null어떤 호출하지 않고, Integer생성자를.
Ted Hopp

40

Java 컴파일러 true ? null : 0Integer표현식으로 해석 되며 암시 적으로로 변환 int하여을 제공 할 수 있다고 생각합니다 NullPointerException.

두 번째 경우를 들어, 표현은 null특별하다 널 유형 참조 코드가 있으므로, return null형식이 일치한다.


2
이것이 자동 권투와 관련이 있다고 생각합니까? 아마도 첫 번째 리턴은 Java 5 이전에 컴파일 되지 않았을 것입니다 .
마이클 맥고완

Eclipse의 준수 레벨을 5 이전으로 설정 한 경우에 해당되는 @Michael입니다.
Jonathon Faust

@Michael : 이것은 자동 복싱처럼 보입니다 (Java에 익숙하지 않아서 더 명확한 문장을 만들 수 없습니다-죄송합니다).
Vlad

1
@ Vlad 컴파일러는 어떻게 다음 true ? null : 0과 같이 해석 Integer합니까? 0먼저 오토 박싱 으로 ??
Marsellus Wallace

1
@Gevorg : Look here : 그렇지 않으면 두 번째와 세 번째 피연산자는 각각 S1과 S2 유형입니다. T1을 복싱 변환을 S1에 적용한 결과 유형으로, T2를 복싱 변환을 S2에 적용한 결과 유형으로 설정합니다. 그리고 다음 텍스트.
Vlad

32

실제로, 그 모든 것은 Java 언어 사양에 설명되어 있습니다.

조건식의 유형은 다음과 같이 결정됩니다.

  • 두 번째 및 세 번째 피연산자가 동일한 유형 (널 유형일 수 있음)을 갖는 경우 이는 조건식의 유형입니다.

따라서 귀하의 "null" (true ? null : 0)은 int 유형을 얻은 다음 정수로 자동 박스됩니다.

이것을 확인하기 위해 이와 같은 것을 시도 (true ? null : null)하면 컴파일러 오류가 발생합니다.


3
그러나 규칙의 해당 조항은 적용되지 않습니다. 두 번째와 세 번째 피연산자는 동일한 유형을 갖지 않습니다 .
Ted Hopp

1
그 대답은 다음과 같습니다.> 그렇지 않으면 두 번째와 세 번째 피연산자는 각각 S1과 S2 유형입니다. T1을 복싱 변환을 S1에 적용한 결과 유형으로, T2를 복싱 변환을 S2에 적용한 결과 유형으로 설정합니다. 조건식의 유형은 캡처 변환 (§5.1.10)을 lub (T1, T2) (§15.12.2.7)에 적용한 결과입니다.
nowaq

나는 그것이 해당 조항이라고 생각합니다. 그런 다음 int함수에서 값 을 반환하기 위해 자동 Unboxing을 적용하려고 시도하여 NPE가 발생합니다.
Ted Hopp

@nowaq 나도 이것을 생각했다. 그러나 "T1을 S1에 복싱 변환을 적용한 결과 유형으로 지정하십시오"라고 명시 적으로 상자 null에 넣으려고하면 다음 Integernew Integer(null);같은 결과를 얻지 NumberFormatException못합니다.
Marsellus Wallace

@Gevorg 권투를 할 때 예외가 발생했기 때문에 여기에서 어떤 결과도 얻지 못할 것이라고 생각합니다. 컴파일러는 그 정의를 따르는 코드를 생성해야 할 의무가 있습니다. 완료하기 전에 예외가 발생합니다.
Voo

25

if명령문 의 경우 null참조는 Integer참조 로 해석되도록 하는 표현식에 참여하지 않으므로 참조 로 취급 되지 않습니다 . 따라서 오류가 더 명확하게 유형 오류 이므로 컴파일 타임에 오류가 쉽게 잡힐 수 있습니다 .

조건부 연산자와 관련하여 Java 언어 사양 §15.25“조건부 연산자 ? :”는 유형 변환이 적용되는 규칙에 따라 다음과 같이 잘 응답합니다.

  • 두 번째 및 세 번째 피연산자가 동일한 유형 (널 유형일 수 있음)을 갖는 경우 이는 조건식의 유형입니다.

    이 없기 때문에 적용 null되지 않습니다 int.

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

    어느 쪽도 있기 때문에 적용되지 않음 nullint없다 booleanBoolean.

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

    이므로 적용 되지 않지만 참조 유형은 아닙니다.nullint

  • 그렇지 않으면 두 번째 및 세 번째 피연산자에 숫자 유형으로 변환 가능한 유형 (§5.1.8)이있는 경우 몇 가지 경우가 있습니다. […]

    적용 : null숫자 유형으로 변환 가능한 것으로 취급되며 §5.1에 정의되어 있습니다. 8“Unboxing Conversion”을 던지십시오 NullPointerException.

0로 자동 박스 된 경우 Integer컴파일러는 Java 언어 사양에 설명 된대로 "삼항 연산자 규칙"의 마지막 경우를 실행합니다. 그것이 사실이라면, 삼항 연산자의 반환 값을 참조 유형 (정수)으로 만드는 null 및 참조 유형을 갖는 동일한 규칙의 사례 3으로 넘어갈 것이라고 믿기가 어렵습니다. .
Marsellus 월러스

@Gevorg-삼항 연산자가 Integer?를 반환한다고 믿기 어려운 이유는 무엇 입니까? 정확히 무슨 일이 일어나고 있는지; int함수에서 를 반환하기 위해 표현식 값을 개봉하여 NPE가 생성됩니다 . 를 반환하도록 함수를 변경하면 아무런 문제없이 Integer반환 null됩니다.
Ted Hopp

2
@TedHopp : Gevorg가 이전 답변의 이전 개정판에 응답했습니다. 불일치를 무시해야합니다.
Jon Purdy

@JonPurdy 내가 그 생각하지 않는다 "A 타입은 숫자 형, 또는이 언 박싱 변환에 의해 숫자 유형으로 변환 할 수있는 참조 형식 인 경우 숫자 형식으로 변환이라고합니다" null이 범주에 빠진다 . 또한 "그렇지 않으면 이진 숫자 승격 (§5.6.2)이 적용됩니다 ... 이진 숫자 승격은 언 박스 변환 (§5.1.8) ..."단계를 수행하여 반환 유형을 결정합니다. 그러나 unboxing 변환은 NPE를 생성하며 이는 삼항 연산자 유형을 결정하려고 시도하는 동안이 아니라 런타임에만 발생합니다. 나는 아직도 혼란스러워 ..
Marsellus Wallace

@Gevorg : Unboxing은 런타임에 발생합니다. 는 null이 유형을 가지고 것처럼 처리됩니다 int만, 실제로 동일합니다 throw new NullPointerException()전부가.
Jon Purdy

11

가장 먼저 염두에 두어야 할 것은 Java 3 진 연산자에는 "유형"이 있으며 이는 두 번째 또는 세 번째 매개 변수의 실제 / 실제 유형에 관계없이 컴파일러가 결정하고 고려하는 것입니다. 몇 가지 요인에 따라 삼항 연산자 유형은 Java 언어 사양 15.26에 설명 된대로 다른 방식으로 결정됩니다.

위의 질문에서 우리는 마지막 경우를 고려해야합니다.

그렇지 않으면, 두 번째 및 세 번째 피연산자는 각각 S1S2 유형 입니다. 하자 T1이 에 권투 변환을 적용 결과 그 유형이 될 S1 및하자가 T2 타입이 될 그에게 권투 변환을 적용 결과 S2 . 조건식의 유형은 캡처 변환 (§5.1.10)을 lub (T1, T2) (§15.12.2.7) 에 적용한 결과입니다 .

캡처 변환 (§5.1.10) 적용을 살펴보면 가장 복잡한 경우 입니다. lub (T1, T2)입니다.

평범한 영어로 그리고 극단적으로 단순화 한 후에 프로세스는 두 번째 및 세 번째 매개 변수의 "최소 공통 수퍼 클래스"(예, LCM 생각)를 계산하는 것으로 설명 할 수 있습니다. 이것은 삼항 연산자 "type"을 줄 것입니다. 다시 말하지만 방금 말한 것은 극단적 인 단순화입니다 (여러 공통 인터페이스를 구현하는 클래스 고려).

예를 들어 다음을 시도하면

long millis = System.currentTimeMillis();
return(true ? new java.sql.Timestamp(millis) : new java.sql.Time(millis));

조건식의 결과 유형은 /에 java.util.Date대한 "최소 공통 수퍼 클래스"이므로TimestampTime 쌍 .

null무엇이든 오토 박스 할 수 있기 때문에 "최소 공통 수퍼 클래스"는Integer 클래스이며 위의 조건식 (삼항 연산자)의 반환 유형이됩니다. 리턴 값은 유형의 널 포인터가 Integer되고 이는 삼항 연산자에 의해 리턴됩니다.

런타임에 Java Virtual Machine이 개봉하면 Integera NullPointerException가 발생합니다. JVM이 함수를 호출하려고하기 때문에 발생합니다 null.intValue().null autoboxing의 결과입니다.

내 의견으로는 (그리고 내 의견은 Java 언어 사양에 없기 때문에 많은 사람들이 어쨌든 잘못 알 것입니다) 컴파일러는 귀하의 질문에 표현을 평가하는 데 열악한 일을합니다. true ? param1 : param2컴파일러가 작성한 것을 고려 하면 첫 번째 매개 변수-- null가 반환되고 컴파일러 오류가 발생 하는지 즉시 결정 해야합니다. 이것은 작성시 while(true){} etc...와 다소 유사 하며 컴파일러는 루프 아래의 코드에 대해 불평하고로 플래그를 지정합니다 Unreachable Statements.

두 번째 경우는 매우 간단 하며이 답변은 이미 너무 깁니다 ...;)

보정:

다른 분석 후 나는 null가치가 무엇이든 박스 / 오토 박스 될 수 있다고 말하는 것이 잘못되었다고 생각합니다 . 클래스 Integer에 대해 말하면, 명시 적 권투는 new Integer(...)생성자 또는 Integer.valueOf(int i);(어딘가 에서이 버전을 찾았 습니다)를 호출하는 것으로 구성 됩니다. 전자는 NumberFormatException(그리고 이것이 일어나지 않습니다) 던지는 반면 두 번째는 불가능하기 때문에 의미 int가 없습니다 null...


1
nullOP의 원래 코드는 박스되지 않습니다. 작동 방식은 다음과 같습니다. 컴파일러는이 null정수형에 대한 참조 라고 가정합니다 . 삼항 표현식 유형에 대한 규칙을 사용하여 전체 표현식이 정수 표현식인지 판별합니다. 그런 다음 1조건을로 평가하는 경우 코드를 자동 상자에 생성합니다 false. 실행하는 동안 조건은로 평가되어 true표현식은로 평가됩니다 null. int함수에서 를 반환하려고 하면 null은 (는) 개봉되어 있습니다. 그런 다음 NPE를 던집니다. (컴파일러는이 대부분을 최적화 할 수 있습니다.)
Ted Hopp

4

실제로 첫 번째 경우에는 컴파일러가 알고 있으므로 식을 평가할 수 Integer있지만 두 번째 경우에는 반환 값 ( null) 의 유형을 결정할 수 없으므로 컴파일 할 수 없습니다. 로 캐스팅 Integer하면 코드가 컴파일됩니다.


2
private int temp() {

    if (true) {
        Integer x = null;
        return x;// since that is fine because of unboxing then the returned value could be null
        //in other words I can say x could be null or new Integer(intValue) or a intValue
    }

    return (true ? null : 0);  //this will be prefectly legal null would be refrence to Integer. The concept is one the returned
    //value can be Integer 
    // then null is accepted to be a variable (-refrence variable-) of Integer
}

0

이건 어때요:

public class ConditionalExpressionType {

    public static void main(String[] args) {

        String s = "";
        s += (true ? 1 : "") instanceof Integer;
        System.out.println(s);

        String t = "";
        t += (!true ? 1 : "") instanceof String;
        System.out.println(t);

    }

}

출력은 true, true입니다.

Eclipse는 조건식에서 1을 자동 상자로 코딩합니다.

내 생각에 컴파일러는 표현식의 반환 유형을 Object 로보 고 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.