Java 7 JVM에서 실행되도록 Java 8 코드를 컴파일 할 수 있습니까?


163

Java 8에는 람다 식과 같은 중요한 새로운 언어 기능이 도입되었습니다.

언어의 이러한 변경으로 인해 역변환기를 사용하지 않고 Java 7 가상 머신에서 실행되지 못하게하는 컴파일 된 바이트 코드의 상당한 변경이 수반됩니까?


답변:


146

아니요, 소스 코드에서 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

4
아니, 나는 그렇게 생각하지 않습니다. Java는 데스크탑 시장에서 작은 점유율을 차지하지만 그 작은 점유율을 꽤 단단히 잡습니다. 그러나 새로운 버전과 기능의 채택을 방해합니다. 사람들이 로컬 Java 설치를 업그레이드 해야하는 것을 피하고 싶기 때문에 작성한 코드에서 Java 8 기능을 꽤 오랫동안 사용할 수 없습니다.
JesperE

왜? "예"는 Java 8 컴파일러가 Java 7 VM에서 실행되도록 Java 8을 컴파일 할 수 있음을 의미합니다.
JesperE

5
이제 나는 당신의 "아니오"가 질문의 본문이 아니라 질문의 헤드 라인에 답하는 것을 본다.
Abdull

58

기본 메소드는 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-Backportstreamsupport )로 제공됩니다.

요약, 기본 방법에는 새로운 JVM 기능이 필요하지만 다른 언어 기능에는 필요하지 않습니다. 그것들을 사용하려면 Java 8로 코드를 컴파일 한 다음 Retrolambda 를 사용 하여 바이트 코드 를 Java 5/6/7 형식으로 변환 해야합니다. 최소한 바이트 코드 버전을 변경해야하며 javac는 허용하지 -source 1.8 -target 1.7않으므로 레트로 번역기가 필요합니다.


3
실제로 타입 주석은 런타임에서 볼 수 있습니다. stackoverflow.com/questions/22374612/…
안티몬

33

내가 아는 한 JDK 8에서 이러한 변경 사항 중 아무것도 새로운 바이트 코드를 추가하지 않아도됩니다. 람다 계측의 일부는 invokeDynamic(JDK 7에 이미 존재)를 사용하여 수행됩니다 . 따라서 JVM 명령어 세트 관점에서 코드베이스가 호환되지 않아야하는 것은 없습니다. 그러나 JDK 8의 코드를 이전 JDK에서 컴파일 / 실행하기가 어려울 수있는 API 관련 및 컴파일러 개선이 많이 있습니다 (그러나 나는 시도하지 않았습니다).

다음 참조 자료는 람다와 관련된 변경 사항이 어떻게 계측되는지 이해하는 데 도움이 될 수 있습니다.

이것들은 후드 아래에서 물건이 어떻게 계측되는지 자세히 설명합니다. 아마도 당신은 거기에 당신의 질문에 대한 답변을 찾을 수 있습니다.


7
새로운 바이트 코드는 없지만 새로운 구조. 검증 기가 펑크합니다.
Jonathan S. Fisher

12
좋은 예는 인터페이스입니다. 이제 메소드를 포함 할 수 있습니다. Java7 검증기는이를 처리 할 수 ​​없습니다. 모든 이전 바이트 코드가 사용되지만 새로운 방식으로 사용됩니다.
Jonathan S. Fisher

1
많은 언어 기능을 가진 스칼라 컴파일러가 jdk5의 대상 jvm 릴리스를 달성하는 방법이 궁금합니다.
Marinos An

1
@MarinosAn 정확히 무엇을 의미합니까? 예를 들어 구체적인 방법을 포함하는 특성을 가진 MI는 class C extends A with B일반 인터페이스를 구현 A하고 B및 동반자 클래스 A$classB$class. class는 C단순히 메서드를 정적 도우미 클래스로 전달합니다. 자체 유형은 전혀 적용되지 않으며 컴파일 타임에 람다는 내부 클래스를 추상화하기 위해 변환되므로 new D with A with B표현식도 마찬가지입니다 . 패턴 매칭은 if-else 구조입니다. 비 현장 반품? 람다에서 try-catch 메커니즘. 남은 것이 있습니까? (흥미롭게도, 나의 스 칼락은 1.6이 기본값이라고 말합니다)
Adowrath

1
물론, 셀프 타입 등은 특수 클래스 속성과 주석으로 인코딩되어 스칼라가 이미 컴파일 된 클래스를 사용할 때 규칙을 사용하고 적용 할 수 있습니다.
Adowrath


-5

그러면 -source 1.7 -target 1.7컴파일 할 수 있습니다 . 그러나 람다와 같은 Java 8 특정 기능이 있으면 컴파일되지 않습니다.


이 질문은 새로운 언어 기능의 사용을 명시 적으로 제기하므로 -source 1.7날지 않을 것입니다.
toolforger
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.