IT

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

lottoking 2020. 5. 16. 10:25
반응형

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


다음 스 니펫에서 간단한 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()메소드 에서 와 같이) 명령문으로 삼항 연산자를 나타내면 컴파일 타임 오류 발생하는 경우에도 같은 문제가 발생합니다! 왜?


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


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

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


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

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

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

따라서 "null" (true ? null : 0)은 int 유형을 얻은 다음 Integer로 자동 상자 화됩니다.

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


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

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

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

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

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

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

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

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

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

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

The first thing to keep in mind is that Java ternary operators have a "type", and that this is what the compiler will determine and consider no matter what the actual/real types of the second or third parameter are. Depending on several factors the ternary operator type is determined in different ways as illustrated in the Java Language Specification 15.26

In the question above we should consider the last case:

Otherwise, the second and third operands are of types S1 and S2 respectively. Let T1 be the type that results from applying boxing conversion to S1, and let T2 be the type that results from applying boxing conversion to S2. The type of the conditional expression is the result of applying capture conversion (§5.1.10) to lub(T1, T2) (§15.12.2.7).

This is by far the most complex case once you take a look at applying capture conversion (§5.1.10) and most of all at lub(T1, T2).

In plain English and after an extreme simplification we can describe the process as calculating the "Least Common Superclass" (yes, think of the LCM) of the second and third parameters. This will give us the ternary operator "type". Again, what I just said is an extreme simplification (consider classes that implement multiple common interfaces).

For example, if you try the following:

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

You'll notice that resulting type of the conditional expression is java.util.Date since it's the "Least Common Superclass" for the Timestamp/Time pair.

Since null can be autoboxed to anything, the "Least Common Superclass" is the Integer class and this will be the return type of the conditional expression (ternary operator) above. The return value will then be a null pointer of type Integer and that is what will be returned by the ternary operator.

At runtime, when the Java Virtual Machine unboxes the Integer a NullPointerException is thrown. This happens because the JVM attempts to invoke the function null.intValue(), where null is the result of autoboxing.

In my opinion (and since my opinion is not in the Java Language Specification many people will find it wrong anyway) the compiler does a poor job in evaluating the expression in your question. Given that you wrote true ? param1 : param2 the compiler should determine right away that the first parameter -null- will be returned and it should generate a compiler error. This is somewhat similar to when you write while(true){} etc... and the compiler complains about the code underneath the loop and flags it with Unreachable Statements.

Your second case is pretty straightforward and this answer is already too long... ;)

CORRECTION:

After another analysis I believe that I was wrong to say that a null value can be boxed/autoboxed to anything. Talking about the class Integer, explicit boxing consists in invoking the new Integer(...) constructor or maybe the Integer.valueOf(int i); (I found this version somewhere). The former would throw a NumberFormatException (and this does not happen) while the second would just not make sense since an int cannot be null...


Actually, in the first case the expression can be evaluated, since the compiler knows, that it must be evaluated as an Integer, however in the second case the type of the return value (null) can not be determined, so it can not be compiled. If you cast it to Integer, the code will compile.


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
}

How about this:

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);

    }

}

The output is true, true.

Eclipse color codes the 1 in the conditional expression as autoboxed.

My guess is the compiler is seeing the return type of the expression as Object.

참고URL : https://stackoverflow.com/questions/8098953/returning-null-as-an-int-permitted-with-ternary-operator-but-not-if-statement

반응형