Java 8 представляет важные новые языковые функции, такие как лямбда-выражения.
Эти изменения в языке сопровождаются такими значительными изменениями в скомпилированном байт-коде, которые могут помешать его запуску на виртуальной машине Java 7 без использования какого-либо ретро-переводчика?
Ответы:
Нет, использование функций 1.8 в вашем исходном коде требует от вас целевой виртуальной машины 1.8. Я только что попробовал новую версию Java 8 и попытался скомпилировать
-target 1.7 -source 1.8
, и компилятор отказался:источник
Методы по умолчанию требуют таких изменений в байт-коде и JVM, которые было бы невозможно сделать в Java 7. Верификатор байт-кода в Java 7 и ниже отклонит интерфейсы с телами методов (за исключением метода статического инициализатора). Попытка эмулировать методы по умолчанию со статическими методами на стороне вызывающего не приведет к тем же результатам, потому что методы по умолчанию могут быть переопределены в подклассах. Retrolambda имеет ограниченную поддержку бэкпортинга методов по умолчанию, но никогда не может быть полностью перенесен обратно, потому что действительно требует новых функций JVM.
Lambdas мог бы работать на Java 7 как есть, если бы там просто существовали необходимые классы API. Инструкция invokedynamic существует в Java 7, но было бы возможно реализовать лямбда-выражения так, чтобы она генерировала лямбда-классы во время компиляции (ранние сборки JDK 8 делали это таким образом), и в этом случае она работала бы на любой версии Java. (Oracle решила использовать invokedynamic для лямбда-выражений для проверки будущего; возможно, однажды JVM будет иметь первоклассные функции, поэтому затем можно изменить invokedynamic, чтобы использовать их вместо генерации класса для каждой лямбды, что повысит производительность.) Что делает 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 ).
Summa summarum, методы по умолчанию требуют новых функций JVM, но другие языковые функции не делают. Если вы хотите их использовать, вам нужно скомпилировать код в Java 8, а затем преобразовать байт-код с Retrolambda в формат Java 5/6/7. Как минимум, версия байт-кода должна быть изменена, а javac запрещает
-source 1.8 -target 1.7
использование ретротранслятора.источник
Насколько я знаю, ни одно из этих изменений в JDK 8 не требовало добавления новых байт-кодов. Часть лямбда-инструментария выполняется с использованием
invokeDynamic
(которое уже существует в JDK 7). Таким образом, с точки зрения набора инструкций JVM ничто не должно сделать кодовую базу несовместимой. Тем не менее, существует множество улучшений, связанных с API и компилятором, которые могут затруднить компиляцию / запуск кода из JDK 8 под предыдущими JDK (но я этого не пробовал).Может быть, следующий справочный материал может помочь как-то обогатить понимание того, как проводятся изменения, связанные с лямбдой.
Они подробно объясняют, как все инструменты под капотом. Возможно, вы сможете найти ответ на свои вопросы там.
источник
class C extends A with B
, реализуется с обычными интерфейсамиA
иB
и сопутствующими классамиA$class
иB$class
. КлассC
просто перенаправляет методы в статические сопутствующие классы. Самостоятельные типы вообще не применяются, лямбды передаются во время компиляции в абстрактные внутренние классы, как иnew D with A with B
выражения. Сопоставление с образцом - это связка структур if-else. Нелокальные возвращения? механизм try-catch от лямбды. Что-нибудь осталось? (Интересно, что мой скаляр говорит, что 1.6 по умолчанию)Если вы хотите использовать «ретротранслятор», попробуйте отличную Retrolambda от Esko Luontola: https://github.com/orfjackal/retrolambda
источник
Вы можете сделать,
-source 1.7 -target 1.7
то он будет компилироваться. Но он не скомпилируется, если у вас есть специфические особенности Java 8, такие как лямбдыисточник
-source 1.7
не будет летать.