최근에 저는 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...
이 컨텍스트에서 삼항 표현식을 사용하는 것과 구문을 사용하는 것의 차이점은 무엇입니까 ?
답변
피연산자의 유형에 대해 생각하면 문제가 더 분명해집니다.
this.method != null ? this.method : this.constructor
같은 즉, 모두에게 가장 전문 유형의 일반적인 두 피연산자의 가장 전문 일반적인 유형, 입력 한 this.method
과 this.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을 개발하는 동안 일시적인 문제였던 것 같습니다.
답변
이것은 공식 JDK-8이 출시되기 거의 1 년 전인 2013 년 5 월 3 일 에 꽤 오래된 커밋 에서 도입 되었습니다. 그 당시에는 컴파일러가 과도하게 개발되었으므로 이러한 호환성 문제가 발생할 수 있습니다. Spring 팀은 실제로 컴파일러 문제 임에도 불구하고 JDK-8 빌드를 테스트하고 문제를 해결하려고 시도했습니다. JDK-8 공식 출시로 이것은 관련이 없습니다. 이제이 코드의 삼항 연산자는 예상대로 잘 작동합니다 ( Executable
컴파일 된 .class-file의 클래스에 대한 참조가 없습니다 ).
현재 JDK-9에서도 비슷한 현상이 나타납니다. JDK-8에서 잘 컴파일 할 수있는 일부 코드는 JDK-9 javac에서 실패했습니다. 이러한 문제의 대부분은 출시 때까지 해결 될 것입니다.
답변
가장 큰 차이점은 if
else
블록은 명령문 이고 삼항 ( 자바 에서 조건부 연산자 라고도 함 )은 표현식이라는 것 입니다.
문은 같은 일을 할 수있는 return
제어 경로의 일부에 호출자에게. 표현은 할당에 사용할 수 있습니다 :
int n = condition ? 3 : 2;
따라서 조건 뒤에있는 삼항의 두 표현식은 동일한 유형으로 강제 변환되어야합니다. 이로 인해 특히 자동 박싱 및 자동 참조 캐스팅과 함께 Java에서 이상한 효과가 발생할 수 있습니다. 게시 된 코드의 주석이 참조하는 내용입니다. 귀하의 경우 표현식의 강제는 java.lang.reflect.Executable
유형 ( 가장 특수한 유형 이므로)이며 이전 버전의 Java에는 존재하지 않습니다.
스타일 적으로 if
else
코드가 문장과 같은 경우 블록을 사용하고 표현식과 같은 경우 삼항을 사용해야합니다.
물론 if
else
람다 함수를 사용하면 블록이 표현식처럼 동작하도록 만들 수 있습니다 .
답변
삼항 표현식의 반환 값 유형은 Java 8에 설명 된대로 변경된 상위 클래스의 영향을받습니다.
왜 캐스트가 작성되지 않았는지 알기 어렵습니다.