Можно ли скомпилировать код Java 8 для работы на Java 7 JVM?

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

Являются ли эти изменения на языке сопровождаемыми такими значительными изменениями в компилированном байт-коде, который предотвратил бы его запуск на виртуальной машине Java 7 без использования какого-либо ретротранслятора?

Ответ 1

Нет, использование 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

Ответ 2

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

Lambdas может работать на Java 7 как есть, если там будут существовать необходимые классы API. Invokedynamic инструкция существует на Java 7, но было бы возможно реализовать lambdas, чтобы он генерировал лямбда-классы во время компиляции (ранние сборки JDK 8 сделали это именно так), и в этом случае она будет работать на любой Java-версии. (Oracle решила использовать invokedynamic для lambdas для будущей проверки, возможно, однажды JVM будет иметь первоклассные функции, поэтому вместо нее можно использовать invokedynamic, чтобы использовать их вместо генерации класса для каждой лямбда, что повышает производительность). Что делает Retrolambda что он обрабатывает все эти invokedynamic инструкции и заменяет их анонимными классами; то же, что и Java 8 во время выполнения, когда вызов lddba invokedynamic называется первым.

Повторяющиеся аннотации - это просто синтаксический сахар. Они являются байт-кодами, совместимыми с предыдущими версиями. В 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

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

Возможно, следующий справочный материал может помочь каким-то образом обогатить понимание того, как инструменты, связанные с лямбдой, становятся инструментальными.

Они подробно объясняют, как вещи под управлением капота. Возможно, вы можете найти ответ на свои вопросы.

Ответ 4

Если вы хотите использовать "ретротранслятор", попробуйте Esko Luontola отлично Retrolambda: https://github.com/orfjackal/retrolambda

Ответ 5

Вы можете сделать -source 1.7 -target 1.7, затем он скомпилируется. Но он не будет компилироваться, если у вас есть java 8 специальных функций, таких как lambdas