Чи може бути скомпільований код Java 8 для роботи на Java 7 JVM?


163

Java 8 представляє важливі нові мовні функції, такі як лямбда-вирази.

Чи супроводжуються ці зміни мови такими суттєвими змінами у складеному байтовому коді, які б перешкоджали його запуску на віртуальній машині Java 7 без використання якогось ретротранслятора?


Відповіді:


146

Ні, використання 1.8 функцій у вихідному коді вимагає націлити 1,8 ВМ. Я просто спробував новий реліз 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 8 в коді, який я пишу досить довго, оскільки я хочу уникати того, щоб люди потребували оновлення локальної установки Java.
JesperE

Чому? "Так" означає, що Java 8 може бути компільована для роботи на Java 7 VM, що неправильно відповідно до компілятора Java 8.
JesperE

5
Тепер я бачу: Ваше "Ні" відповідає заголовку питання, а не тілу питання.
Абдул

58

Методи за замовчуванням вимагають таких змін у байт-коді та JVM, що їх було б неможливо зробити на Java 7. Перевірявач байт-коду Java 7 і нижче буде відхиляти інтерфейси з органами методів (за винятком статичного методу ініціалізатора). Спроба емуляції методів за замовчуванням статичними методами на стороні виклику не дала б однакових результатів, оскільки методи за замовчуванням можуть бути замінені в підкласах. Retrolambda має обмежену підтримку методів підтримки за замовчуванням, але він ніколи не може бути повністю підтримуваний, оскільки він справді вимагає нових функцій JVM.

Lambdas міг би працювати на Java 7 як є, якщо потрібні класи API просто існували б там. Викликана динамічна інструкція існує на Java 7, але можна було б реалізувати лямбдати так, щоб вона генерувала класи лямбда в час компіляції (ранні JDK 8 побудови робили це так), і в цьому випадку вона буде працювати в будь-якій версії Java. (Oracle вирішив використати інкекединаміку для лямбда для подальшої перевірки; можливо, одного дня JVM матиме функції першого класу, тож виклик динаміки можна буде змінити, щоб використовувати їх замість того, щоб генерувати клас для кожної лямбда, покращуючи тим самим продуктивність.) Що робить Retrolambda - це що він обробляє всі ці викликані динамічні інструкції та замінює їх анонімними класами; те саме, що робить Java 8 під час виконання, коли lamdba, що викликає динаміку, називається вперше.

Повторення анотацій - це лише синтаксичний цукор. Вони є байт-кодом, сумісним з попередніми версіями. У Java 7 вам просто потрібно реалізувати собі допоміжні методи (наприклад, getAnnotationsByType ), які приховують деталі реалізації анотації на контейнері, яка містить повторні анотації.

AFAIK, анотації типів існують лише під час компіляції, тому вони не потребують змін байтового коду, тому просто зміни кількості версій байт-коду класів, складених 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тому потрібен ретротранслятор.


3
Насправді анотації типу можуть бути видимими під час виконання. stackoverflow.com/questions/22374612/…
Сурма

33

Наскільки мені відомо, жодна з цих змін у JDK 8 не вимагала додавання нових байткодів. Частина інструментів лямбда проводиться за допомогою invokeDynamic(які вже існують у JDK 7). Отже, з точки зору набору інструкцій JVM, ніщо не повинно робити кодову базу несумісною. Однак існує багато вдосконалених API та вдосконалень компілятора, які могли б ускладнити компіляцію / запуск коду з JDK 8 за попередніми JDK (але я цього не пробував).

Можливо, наступний довідковий матеріал може допомогти якось збагатити розуміння того, як вносяться зміни, пов'язані з лямбда.

Вони докладно пояснюють, як справи інструментуються під капотом. Можливо, там ви знайдете відповідь на свої запитання.


7
Не нових байткодів, а нових структур. Верифікатор буде нудити.
Джонатан С. Фішер

12
Хороший приклад - інтерфейси. Тепер вони можуть містити методи. Перевіряльник Java7 не обладнаний для цього. Всі старі байткоди використовуються, але по-новому.
Джонатан С. Фішер

1
Цікаво, як компілятор Scala з такою кількістю мовних особливостей може досягти цільового випуску jvm навіть jdk5.
Марінос Ан

1
@MarinosAn Що саме ти маєш на увазі? MI з рисами , які містять конкретні методи, наприклад class C extends A with B, реалізується з нормальним інтерфейсом Aі Bі класів - компаньйонів A$classі B$class. клас Cпросто перенаправляє методи на класичні супутникові класи. Самотипи взагалі не застосовуються, лямбди перетворюються на час компіляції в абстрактні внутрішні класи, так це є new D with A with Bвиразом. Зіставлення шаблонів - це купа структур, що інше. Не місцеві повернення? випробувальний механізм від лямбда. Що залишилось? (Цікаво, що мій скалак каже, що за замовчуванням 1.6 є)
Adowrath

1
Звичайно, власні типи тощо кодуються в атрибути та примітки спеціальних класів, щоб scalac міг використовувати та застосовувати правила при використанні вже складених класів.
Adowrath


-5

Ви можете зробити, -source 1.7 -target 1.7тоді він складе. Але він не компілюється, якщо у вас є особливості Java 8, як лямбда


Питання явно використовує нові мовні функції, тому -source 1.7не буде літати.
toolforger
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.