Java 8에는 람다 식과 같은 중요한 새로운 언어 기능이 도입되었습니다.
언어의 이러한 변경으로 인해 역변환기를 사용하지 않고 Java 7 가상 머신에서 실행되지 못하게하는 컴파일 된 바이트 코드의 상당한 변경이 수반됩니까?
Java 8에는 람다 식과 같은 중요한 새로운 언어 기능이 도입되었습니다.
언어의 이러한 변경으로 인해 역변환기를 사용하지 않고 Java 7 가상 머신에서 실행되지 못하게하는 컴파일 된 바이트 코드의 상당한 변경이 수반됩니까?
답변:
아니요, 소스 코드에서 1.8 기능을 사용하려면 1.8 VM을 타겟팅해야합니다. 방금 새로운 Java 8 릴리스를 시도하고로 컴파일을 시도했지만 -target 1.7 -source 1.8
컴파일러는 다음을 거부합니다.
$ javac Test -source 1.8 -target 1.7
javac: source release 1.8 requires target release 1.8
기본 메소드는 Java 7에서 불가능했던 바이트 코드 및 JVM에 대한 변경이 필요합니다. Java 7 이하의 바이트 코드 검증기는 메소드 본문과의 인터페이스를 거부합니다 (정적 초기화 프로그램 메소드 제외). 호출자 측에서 정적 메소드를 사용하여 기본 메소드를 에뮬레이트하려고 시도해도 동일한 결과가 생성되지 않습니다. 기본 메소드는 서브 클래스에서 대체 될 수 있기 때문입니다. Retrolambda 는 백 포트 기본 방법에 대한 지원이 제한적이지만 새로운 JVM 기능이 필요하므로 완전히 백 포트 할 수 없습니다.
필요한 API 클래스가 존재한다면 람다는 Java 7을 그대로 실행할 수 있습니다. invokedynamic 명령어는 Java 7에 존재하지만 컴파일 타임에 람다 클래스를 생성하도록 람다를 구현할 수 있었을 것입니다 (이전의 JDK 8 빌드는 그렇게 했음). (오라클은 향후 교정을 위해 람다에 대해 invokedynamic을 사용하기로 결정했습니다. 언젠가 JVM에 일급 기능이 있으므로 모든 람다에 대해 클래스를 생성하는 대신 invokedynamic을 사용하여 성능을 향상시킬 수 있습니다.) Retrolambda의 기능은 다음과 같습니다. 모든 호출 동적 명령을 처리하고 익명 클래스로 대체합니다. lamdba invokedynamic이 처음 호출 될 때 Java 8이 런타임시 수행하는 것과 동일합니다.
주석 반복 은 단지 구문 설탕입니다. 이전 버전과 호환되는 바이트 코드입니다. Java 7에서는 반복 주석이 포함 된 컨테이너 주석의 구현 세부 사항을 숨기는 도우미 메소드 (예 : getAnnotationsByType ) 를 구현해야합니다 .
AFAIK, Type Annotations 는 컴파일 타임에만 존재하므로 바이트 코드 변경이 필요하지 않으므로 Java 8 컴파일 클래스의 바이트 코드 버전 번호를 변경하면 Java 7에서 작동하기에 충분해야합니다.
메소드 매개 변수 이름 은 Java 7의 바이트 코드에 존재하므로 호환됩니다. 메소드의 바이트 코드를 읽고 메소드의 디버그 정보에서 로컬 변수 이름을 보면 액세스 할 수 있습니다. 예를 들어 Spring Framework는 @PathVariable 을 구현하기 위해 정확하게 그렇게하므로 호출 할 수있는 라이브러리 메소드가있을 수 있습니다. 추상 인터페이스 메소드에는 메소드 본문이 없으므로 해당 디버그 정보는 Java 7의 인터페이스 메소드에 대해 존재하지 않으며 AFAIK는 Java 8에 존재하지 않습니다.
다른 새로운 기능 은 대부분 새로운 API, HotSpot 및 툴링 개선입니다. 새로운 API 중 일부는 타사 라이브러리 (예 : ThreeTen-Backport 및 streamsupport )로 제공됩니다.
요약, 기본 방법에는 새로운 JVM 기능이 필요하지만 다른 언어 기능에는 필요하지 않습니다. 그것들을 사용하려면 Java 8로 코드를 컴파일 한 다음 Retrolambda 를 사용 하여 바이트 코드 를 Java 5/6/7 형식으로 변환 해야합니다. 최소한 바이트 코드 버전을 변경해야하며 javac는 허용하지 -source 1.8 -target 1.7
않으므로 레트로 번역기가 필요합니다.
내가 아는 한 JDK 8에서 이러한 변경 사항 중 아무것도 새로운 바이트 코드를 추가하지 않아도됩니다. 람다 계측의 일부는 invokeDynamic
(JDK 7에 이미 존재)를 사용하여 수행됩니다 . 따라서 JVM 명령어 세트 관점에서 코드베이스가 호환되지 않아야하는 것은 없습니다. 그러나 JDK 8의 코드를 이전 JDK에서 컴파일 / 실행하기가 어려울 수있는 API 관련 및 컴파일러 개선이 많이 있습니다 (그러나 나는 시도하지 않았습니다).
다음 참조 자료는 람다와 관련된 변경 사항이 어떻게 계측되는지 이해하는 데 도움이 될 수 있습니다.
이것들은 후드 아래에서 물건이 어떻게 계측되는지 자세히 설명합니다. 아마도 당신은 거기에 당신의 질문에 대한 답변을 찾을 수 있습니다.
class C extends A with B
일반 인터페이스를 구현 A
하고 B
및 동반자 클래스 A$class
와 B$class
. class는 C
단순히 메서드를 정적 도우미 클래스로 전달합니다. 자체 유형은 전혀 적용되지 않으며 컴파일 타임에 람다는 내부 클래스를 추상화하기 위해 변환되므로 new D with A with B
표현식도 마찬가지입니다 . 패턴 매칭은 if-else 구조입니다. 비 현장 반품? 람다에서 try-catch 메커니즘. 남은 것이 있습니까? (흥미롭게도, 나의 스 칼락은 1.6이 기본값이라고 말합니다)
"역사 번역기"를 사용하고자한다면 Esko Luontola의 훌륭한 Retrolambda를 사용해보십시오 : https://github.com/orfjackal/retrolambda
그러면 -source 1.7 -target 1.7
컴파일 할 수 있습니다 . 그러나 람다와 같은 Java 8 특정 기능이 있으면 컴파일되지 않습니다.
-source 1.7
날지 않을 것입니다.