Czy kod Javy 8 można skompilować do działania na Javie 7 JVM?

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 jego uruchomienie na maszynie wirtualnej Java 7 bez użycia retrotranslatora?

Author: Arto Bendiken, 2013-04-22

5 answers

Nie, użycie funkcji 1.8 w kodzie źródłowym wymaga kierowania na maszynę wirtualną 1.8. Po prostu próbowałem nowego wydania Javy 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
 123
Author: JesperE,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-18 22:30:24

Metody domyślne wymagają takich zmian w kodzie bajtowym i JVM, które byłyby niemożliwe do wykonania w Javie 7. Weryfikator kodu bajtowego w Java 7 i niżej odrzuci interfejsy z ciałami metod (z wyjątkiem metody inicjalizacji statycznej). Próba emulowania domyślnych metod ze statycznymi metodami po stronie wywołującej nie da takich samych wyników, ponieważ domyślne metody mogą być nadpisywane w podklasach. Retrolambda ma ograniczone wsparcie dla backportowania domyślnych metod, ale nigdy nie może być w pełni backportowany, ponieważ naprawdę wymaga nowych funkcji JVM.

Lambda może działać na Javie 7 tak, jak jest, jeśli potrzebne klasy API po prostu tam istnieją. Instrukcja invokedynamic istnieje w Javie 7, ale możliwe byłoby zaimplementowanie lambda w taki sposób, że generuje ona klasy lambda w czasie kompilacji (wczesne kompilacje JDK 8 robiły to w ten sposób). (Oracle zdecydowało się użyć invokedynamic dla Lambda do przyszłych proofingu; może jeden day JVM będzie miał funkcje pierwszej klasy, więc wtedy invokedynamic może być zmieniony, aby używać ich zamiast generować klasę dla każdego lambda, poprawiając w ten sposób wydajność.) Retrolambda przetwarza wszystkie wywołane instrukcje i zastępuje je anonimowymi klasami; to samo, co robi Java 8 podczas wykonywania, gdy lamdba wywołane jest po raz pierwszy.

Powtarzanie adnotacji jest tylko cukrem składniowym. Są zgodne bajtowo z poprzednimi wersje. W Javie 7 wystarczy zaimplementować samodzielnie metody pomocnicze (np. getAnnotationsByType), które ukrywają szczegóły implementacji adnotacji kontenera, która zawiera powtarzające się adnotacje.

AFAIK, adnotacje typu istnieją tylko w czasie kompilacji, więc nie powinny wymagać zmian kodu bajtowego, więc wystarczy zmienić numer wersji kodu bajtowego klas skompilowanych w Javie 8, aby działały na Javie 7.

Parametr metody nazwy istnieją w bajtowym kodzie z Javą 7, więc jest to również zgodne. Można uzyskać do nich dostęp, odczytując kod bajtowy metody i patrząc na nazwy lokalnych zmiennych w informacji 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ą ciała metody, informacje o debugowaniu nie istnieją dla metod interfejsu w Javie 7, a AFAIK ani na Javie 8.

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

Summa summarum, domyślne metody 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ć bajt za pomocą Retrolambda do Javy 5/6/7 format. Co najmniej wersja kodu bajtowego musi zostać zmieniona, a javac wyłącza -source 1.8 -target 1.7, więc wymagany jest retrotranslator.

 53
Author: Esko Luontola,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-03 00:16:28

Z tego co wiem żadna z tych zmian w JDK 8 nie wymagała dodania nowych bajtekodów. Część oprzyrządowania 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 sprawić, że baza kodowa będzie niezgodna. Istnieje jednak wiele powiązanych z API i ulepszeń kompilatora, które mogłyby utrudnić kompilowanie/uruchamianie kodu z JDK 8 Pod poprzednimi JDK (ale nie próbowałem tego).

Może następujące materiał referencyjny może w jakiś sposób pomóc wzbogacić zrozumienie sposobu instrumentowania zmian związanych z lambda.

Wyjaśniają one szczegółowo, w jaki sposób rzeczy są oprzyrządowane pod maską. Być może znajdziesz tam odpowiedź na swoje pytania.
 31
Author: Edwin Dalorzo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-07 11:17:50

Jeśli chcesz użyć "retrotranslatora", wypróbuj doskonałą Retrolambdę Esko Luontola: https://github.com/orfjackal/retrolambda

 11
Author: Stefan Zobel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-17 09:39:19

Możesz zrobić -source 1.7 -target 1.7 wtedy się skompiluje. Ale nie skompiluje się, jeśli masz specyficzne funkcje java 8, takie jak lambdas

 -5
Author: kalgecin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-20 15:33:38