자바 삼항 연산자 대 <JDK8 호환성의 if / else


113

최근에 저는 Spring Framework의 소스 코드를 읽고 있습니다. 내가 이해할 수없는 것이 여기에 있습니다.

public Member getMember() {
    // NOTE: no ternary expression to retain JDK <8 compatibility even when using
    // the JDK 8 compiler (potentially selecting java.lang.reflect.Executable
    // as common type, with that new base class not available on older JDKs)
    if (this.method != null) {
        return this.method;
    }
    else {
        return this.constructor;
    }
}

이 메소드는 클래스의 멤버입니다. org.springframework.core.MethodParameter . 주석은 어렵지만 코드는 이해하기 쉽습니다.

참고 : JDK 8 컴파일러를 사용하는 경우에도 JDK <8 호환성을 유지하기위한 삼항 표현식이 없습니다 (잠재적으로 java.lang.reflect.Executable 이전 JDK에서는 사용할 수없는 새 기본 클래스와 함께 공통 유형으로 으로 선택됨).

if...else...이 컨텍스트에서 삼항 표현식을 사용하는 것과 구문을 사용하는 것의 차이점은 무엇입니까 ?

답변:


103

피연산자의 유형에 대해 생각하면 문제가 더 분명해집니다.

this.method != null ? this.method : this.constructor

같은 즉, 모두에게 가장 전문 유형의 일반적인 두 피연산자의 가장 전문 일반적인 유형, 입력 한 this.methodthis.constructor.

Java 7에서는 이것 java.lang.reflect.Member이지만 Java 8 클래스 라이브러리 java.lang.reflect.Executable는 일반보다 더 전문화 된 새로운 유형 을 도입합니다 Member. 따라서 자바 8 클래스 라이브러리와 삼항 식의 결과 형식입니다 Executable보다는 Member.

Java 8 컴파일러의 일부 (사전 릴리스) 버전은 Executable삼항 연산자를 컴파일 할 때 생성 된 코드 내부에 대한 명시 적 참조를 생성 한 것으로 보입니다 . 이것은 클래스로드를 트리거하므로 JDK ≥ 8에 대해서만 존재하기 ClassNotFoundException때문에 클래스 라이브러리 <JDK 8로 실행될 때 런타임 에 차례로 발생합니다 Executable.

이 답변 에서 Tagir Valeev가 언급했듯이 이것은 실제로 JDK 8의 시험판 버전의 버그이며 이후 수정되었으므로 if-else해결 방법과 설명 주석이 모두 사용되지 않습니다.

추가 참고 사항 : 이 컴파일러 버그가 Java 8 이전에 존재했다는 결론에 도달 할 수 있습니다 . 그러나 OpenJDK 7에서 삼진 용으로 생성 된 바이트 코드는 OpenJDK 8에서 생성 된 바이트 코드와 동일합니다. 표현식은 런타임에 완전히 언급되지 않습니다. 코드는 실제로 추가 검사없이 테스트, 분기,로드, 반환입니다. 따라서 이것은 (더 이상) 문제가 아니며 실제로 Java 8을 개발하는 동안 일시적인 문제였던 것 같습니다.


1
그런 다음 JDK 1.8로 컴파일 된 코드를 JDK 1.7에서 어떻게 실행할 수 있습니까? 낮은 버전의 JDK로 컴파일 된 코드가 문제없이 높은 버전의 JDK에서 실행될 수 있다는 것을 알고 있습니다.
jddxf 2015 년

1
@jddxf 적절한 클래스 파일 버전을 지정하고 이후 버전에서 사용할 수없는 기능을 사용하지 않는 한 모든 것이 정상입니다. 그러나 이러한 사용이이 경우와 같이 암시 적으로 발생하면 문제가 발생할 수 있습니다.
dhke 2015 년

13
@jddxf, -source / -target javac 옵션 사용
Tagir Valeev 2015

1
특히 dhke과 철저한 설명 준 Tagir Valeev 보낸 사람, 여러분 모두 감사합니다
jddxf

30

이것은 공식 JDK-8이 출시되기 거의 1 년 전인 2013 년 5 월 3 일 에 꽤 오래된 커밋 에서 도입 되었습니다. 그 당시에는 컴파일러가 과도하게 개발되었으므로 이러한 호환성 문제가 발생할 수 있습니다. Spring 팀은 실제로 컴파일러 문제 임에도 불구하고 JDK-8 빌드를 테스트하고 문제를 해결하려고 시도했습니다. JDK-8 공식 출시로 이것은 관련이 없습니다. 이제이 코드의 삼항 연산자는 예상대로 잘 작동합니다 ( Executable컴파일 된 .class-file의 클래스에 대한 참조가 없습니다 ).

현재 JDK-9에서도 비슷한 현상이 나타납니다. JDK-8에서 잘 컴파일 할 수있는 일부 코드는 JDK-9 javac에서 실패했습니다. 이러한 문제의 대부분은 출시 때까지 해결 될 것입니다.


2
+1. 그렇다면 이것은 초기 컴파일러의 버그였습니까? Executable사양의 일부 측면을 위반 하는 동작이라고 언급 했습니까? 아니면 오라클이 이전 버전과의 호환성을 깨지 않고 사양을 준수하는 방식으로이 동작을 변경할 수 있다는 것을 깨달은 것뿐입니까?
ruakh

2
@ruakh, 나는 그것이 버그라고 생각합니다. 바이트 코드 (Java-8 또는 이전 버전)에서는 Executable중간 유형 으로 명시 적으로 캐스트 할 필요가 전혀 없습니다 . Java-8에서는 표현식 유형 추론의 개념이 크게 바뀌었고이 부분은 완전히 다시 작성되었으므로 초기 구현에 버그가 있다는 것은 그리 놀라운 일이 아닙니다.
Tagir Valeev 2015 년

7

가장 큰 차이점은 if else블록은 명령문 이고 삼항 ( 자바 에서 조건부 연산자 라고도 함 )은 표현식이라는 것 입니다.

문은 같은 일을 할 수있는 return제어 경로의 일부에 호출자에게. 표현은 할당에 사용할 수 있습니다 :

int n = condition ? 3 : 2;

따라서 조건 뒤에있는 삼항의 두 표현식은 동일한 유형으로 강제 변환되어야합니다. 이로 인해 특히 자동 박싱 및 자동 참조 캐스팅과 함께 Java에서 이상한 효과가 발생할 수 있습니다. 게시 된 코드의 주석이 참조하는 내용입니다. 귀하의 경우 표현식의 강제는 java.lang.reflect.Executable유형 ( 가장 특수한 유형 이므로)이며 이전 버전의 Java에는 존재하지 않습니다.

스타일 적으로 if else코드가 문장과 같은 경우 블록을 사용하고 표현식과 같은 경우 삼항을 사용해야합니다.

물론 if else람다 함수를 사용하면 블록이 표현식처럼 동작하도록 만들 수 있습니다 .


6

삼항 표현식의 반환 값 유형은 Java 8에 설명 된대로 변경된 상위 클래스의 영향을받습니다.

왜 캐스트가 작성되지 않았는지 알기 어렵습니다.

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