Czy kod Java 8 można skompilować, aby działał na Java 7 JVM?


163

Java 8 wprowadza ważne nowe funkcje językowe, takie jak wyrażenia lambda.

Czy tym zmianom w języku towarzyszą tak znaczące zmiany w skompilowanym kodzie bajtowym, które uniemożliwiłyby uruchomienie go na maszynie wirtualnej Java 7 bez użycia retrotranslatora?


Odpowiedzi:


146

Nie, użycie funkcji 1.8 w kodzie źródłowym wymaga docelowej maszyny wirtualnej 1.8. Właśnie wypróbowałem nową wersję Java 8 i próbowałem kompilować z -target 1.7 -source 1.8, a kompilator odmawia:

$ javac Test -source 1.8 -target 1.7
javac: source release 1.8 requires target release 1.8

4
Nie, nie sądzę, żeby tak było. Java ma niewielki udział w rynku komputerów stacjonarnych, ale utrzymuje ten niewielki udział w dość ścisłej kontroli. Ale utrudnia to przyjmowanie nowych wersji i funkcji. Nie będę mógł używać funkcji Java 8 w kodzie, który piszę przez dłuższy czas, ponieważ chcę uniknąć konieczności aktualizacji lokalnej instalacji Javy.
— JesperE

Czemu? „Tak” oznaczałoby, że Java 8 może zostać skompilowana w celu uruchomienia na maszynie wirtualnej Java 7, co jest niepoprawne według kompilatora Java 8.
— JesperE

5
Teraz widzę: Twoje „Nie” odpowiada nagłówkowi pytania, a nie treści pytania.
— Abdull

58

Domyślne metody wymagają takich zmian w kodzie bajtowym i JVM, których nie byłoby możliwe w Javie 7. Weryfikator kodu bajtowego w Javie 7 i starszych odrzuci interfejsy z treściami metod (z wyjątkiem statycznej metody inicjatora). Próba emulacji metod domyślnych z metodami statycznymi po stronie obiektu wywołującego nie przyniosłaby takich samych wyników, ponieważ metody domyślne można przesłonić w podklasach. Retrolambda ma ograniczone wsparcie dla domyślnych metod backportowania, ale nigdy nie może być w pełni backportowany, ponieważ naprawdę wymaga nowych funkcji JVM.

Lambdy mogłyby działać na Javie 7 w takiej postaci, w jakiej są, gdyby po prostu istniały tam niezbędne klasy API. Instrukcja invokedynamic istnieje w Javie 7, ale byłoby możliwe zaimplementowanie lambda tak, aby generował klasy lambda w czasie kompilacji (wczesne kompilacje JDK 8 robiły to w ten sposób), w którym to przypadku działałaby na dowolnej wersji Javy. (Firma Oracle zdecydowała się na użycie invokedynamic dla lambd do sprawdzania przyszłych zmian; być może pewnego dnia JVM będzie miał funkcje pierwszej klasy, więc wtedy invokedynamic będzie można zmienić tak, aby używał ich zamiast generowania klasy dla każdej lambdy, poprawiając w ten sposób wydajność). że przetwarza wszystkie wywoływane instrukcje dynamiczne i zastępuje je klasami anonimowymi; tak samo, jak robi to Java 8 w czasie wykonywania, gdy wywołanie lamdba dynamic jest wywoływane po raz pierwszy.

Powtarzanie adnotacji to po prostu cukier syntaktyczny. Są zgodne z kodem bajtowym z poprzednimi wersjami. W Javie 7 wystarczyłoby zaimplementować samodzielnie metody pomocnicze (np. GetAnnotationsByType ), które ukrywają szczegóły implementacji adnotacji kontenera zawierającej powtarzające się adnotacje.

AFAIK, adnotacje typu istnieją tylko w czasie kompilacji, więc nie powinny wymagać zmiany kodu bajtowego, więc sama zmiana numeru wersji kodu bajtowego skompilowanych klas Java 8 powinna wystarczyć, aby działały w Javie 7.

Nazwy parametrów metod istnieją w kodzie bajtowym w Javie 7, więc jest to również zgodne. Możesz uzyskać do nich dostęp, czytając kod bajtowy metody i przeglądając nazwy zmiennych lokalnych w informacjach debugowania metody. Na przykład Spring Framework robi dokładnie to, aby zaimplementować @PathVariable , więc prawdopodobnie istnieje metoda biblioteczna, którą możesz wywołać. Ponieważ abstrakcyjne metody interfejsu nie mają treści metody, te informacje debugowania nie istnieją dla metod interfejsu w Javie 7, a AFAIK ani w Javie 8.

Inne nowe funkcje to głównie nowe interfejsy API, ulepszenia HotSpot i narzędzia. Niektóre z nowych interfejsów API są dostępne jako biblioteki innych firm (np. ThreeTen-Backport i streamsupport ).

Summa summarum, metody domyślne wymagają nowych funkcji JVM, ale inne funkcje języka nie. Jeśli chcesz ich użyć, musisz skompilować kod w Javie 8, a następnie przekształcić kod bajtowy w formacie Retrolambda do formatu Java 5/6/7. Co najmniej wersja kodu bajtowego musi zostać zmieniona, a javac nie pozwala na to, -source 1.8 -target 1.7więc wymagany jest retrotranslator.


3
W rzeczywistości adnotacje typu mogą być widoczne w czasie wykonywania. stackoverflow.com/questions/22374612/…
— Antymon

33

O ile wiem, żadna z tych zmian w JDK 8 nie wymagała dodania nowych kodów bajtowych. Część instrumentacji lambda jest wykonywana przy użyciu invokeDynamic(które już istnieją w JDK 7). Tak więc z punktu widzenia zestawu instrukcji JVM nic nie powinno powodować niezgodności bazy kodu. Istnieje jednak wiele ulepszeń związanych z API i kompilatorów, które mogłyby utrudnić kompilację / uruchomienie kodu z JDK 8 w poprzednich JDK (ale nie próbowałem tego).

Być może poniższy materiał referencyjny może w jakiś sposób pomóc w zrozumieniu, w jaki sposób są instrumentowane zmiany związane z lambdą.

Wyjaśniają one szczegółowo, w jaki sposób rzeczy są umieszczone pod maską. Być może znajdziesz tam odpowiedź na swoje pytania.


7
Żadnych nowych kodów bajtowych, ale nowe struktury. Weryfikator zwymiotuje.
— Jonathan S. Fisher

12
Dobrym przykładem są interfejsy. Mogą teraz zawierać metody. Weryfikator Java7 nie jest do tego przystosowany. Wszystkie stare kody bajtowe są używane, ale w nowy sposób.
— Jonathan S. Fisher

1
Zastanawiam się, jak kompilator Scala z tak wieloma funkcjami językowymi może osiągnąć docelowe wydanie jvm nawet jdk5.
— Marinos An

1
@MarinosAn Co dokładnie masz na myśli? MI z cechami, które zawierają konkretne metody, np. class C extends A with BJest zaimplementowany za pomocą normalnych interfejsów Ai Bklas towarzyszących A$classi B$class. class Cpo prostu przekazuje metody do statycznych klas towarzyszących. Typy własne nie są w ogóle wymuszane, wyrażenia lambda są transponowane w czasie kompilacji do abstrakcyjnych klas wewnętrznych, podobnie jak new D with A with Bwyrażenie. Dopasowywanie wzorców to zbiór struktur if-else. Zwroty nielokalne? mechanizm try-catch z lambda. Zostało coś? (Co ciekawe, mój scalak mówi, że domyślnie jest 1,6)
— Adowrath

1
Oczywiście typy własne itp. Są kodowane w specjalnych atrybutach klas i adnotacjach, dzięki czemu scalac może używać i egzekwować reguły podczas używania już skompilowanych klas.
— Adowrath,


-5

Możesz to zrobić, -source 1.7 -target 1.7a następnie się skompilujesz. Ale nie skompiluje się, jeśli masz funkcje specyficzne dla Java 8, takie jak lambdy


Pytanie wyraźnie zakłada użycie nowych funkcji językowych, więc -source 1.7nie polecę.
— toolforger
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.