Java 7 – Здравствуй, динамичность!
Автор: mcfly 15.05.2017 18:46
Несколько моих последних месяцев работы на .NET (больше полугода назад) ознаменовались выходом платформы .NET 4.0, а вместе с ней поддержкой динамических типов, вызова динамичных методов ну и прочих вкусностей.
Как вы знаете, байт-код Java не поддерживает динамического вызова методов. Первым позывом к поддержке динамики в языке стал JSR 223 (Scripting for the Java Platform). Данная спецификация определяла API для вызова Java кода из кода, написанного на динамическом скриптовом языке. Собственно, JSR 223 был реализован в JDK 6, и включен в состав Java SE 6. Однако, разработчикам подобных скриптовых конструкций пришлось испытать немало головной боли, так как байт-код сам по себе был разработан только для статических языков.
Как вы знаете, байт-код Java не поддерживает динамического вызова методов. Первым позывом к поддержке динамики в языке стал JSR 223 (Scripting for the Java Platform). Данная спецификация определяла API для вызова Java кода из кода, написанного на динамическом скриптовом языке. Собственно, JSR 223 был реализован в JDK 6, и включен в состав Java SE 6. Однако, разработчикам подобных скриптовых конструкций пришлось испытать немало головной боли, так как байт-код сам по себе был разработан только для статических языков.
-
Он включает в себя три возможных способа для вызова метода:
- invokestatic
- invokespecial
- invokeinterface или invokevirtual
Особенность этих способов заключается в том, что они позволяют вызывать методы с уже известной сигнатурой. Все вызовы проверяются во время компиляции, что само по себе уже является несовместимым с философией динамичности.
Наконец, в Java 7 мы видим новую особенность – JSR 292 (Supporting Dynamically Typed Languages on the JavaTM Platform). Одним из ключевых изменений стало добавление нового способа вызова из байт-кода – invokedynamic. Это ключевое слово позволяет нам теперь вызывать методы, с сигнатурой, известной только во время выполнения. Данное изменение касается исключительно байт-кода, однако в JDK 7 мы видим новый пакет java.dyn, который также обеспечивает нас возможностью написания динамического кода.
Новый механизм работы с динамическими языками повлек за собой создание новой структуры, обработчик метода (method handles).
MethodHandle
Данный класс позволяет получать и манипулировать ссылкой на определенный метод. Мы можем получить все данные, касающиеся метода, а также вызвать его с любым количеством параметров с помощью функции invoke. Вместе с этим классом, вы также можете использовать рефлексный класс MethodHandles.Lookup, который служит для создания и вызова обработчиков метода.
Следующий пример иллюстрирует данное поведение:
MethodHandles.Lookup lookup = MethodHandles.lookup(); //Задается тип метода, например для String.substring(int): String MethodType methodType = MethodType.methodType(String.class, int.class); MethodHandle methodHandle = lookup.findVirtual(String.class, "substring", mt); String str = (String) methodHandle.invokeExact("smiles", 1); assert(str.equals("miles"));
Более того, производительность данной конструкции практически никак не будет отличаться от прямого вызова.
InvokeDynamic
Данный класс также является нововведением в Java 7. Он сразу выделяется очень необычной особенностью – он не имеет методов. Но более удивительно то, что вы можете вызывать любые методы в нем. Звучит не логично, не правда ли? Мы можем вызывать любые методы в классе, который их не имеет. Например:
String math = InvokeDynamic.combine("a", "+", "b"); Date date = InvokeDynamic.getDate();
Данный код будет удачно скомпилирован. Все эти методы будут проверенны не на этапе компиляции, а на этапе выполнения. Для того, чтобы не получить ошибку времени выполнения, мы должны зарегистрировать определенный bootstrap метод, куда выполняемые InvokeDynamic методы будут в действительности посылаться на выполнение. В дополнение к сказанному, JVM кеширует запросы к bootstrap в целях предотвращения потери производительности, поэтому только первый вызов динамического метода будет искаться. Для того, чтобы произвести регистрацию bootstrap нужно использовать Linkage.registerBootstrapMethod(String methodName) метод.
Однако, на момент написания данной статьи более предпочтительно использование атрибута @BootstrapMethod, который может применяться к типам данных, методам, конструкторам. Приведу пример, частично взятый из официальной документации:
class PrintArgs { private static final MethodHandle printArgs; @BootstrapMethod(value=PrintArgs.class, name="bootstrap") static void test() throws Throwable { // данный запрос будет послан в PrintArgs.bootstrap InvokeDynamic.baz("mix", 2, 3.14); } private static void printArgs(Object... args) { System.out.println(java.util.Arrays.deepToString(args)); } static { MethodHandles.Lookup lookup = MethodHandles.lookup(); printArgs = lookup.findStatic(PrintArgs.class, "printArgs", MethodType.methodType(void.class, Object[].class)); } private static CallSite bootstrap(Class caller, String name, MethodType type) { //адоптируем аргументы, находящиеся в type для printArgs return new CallSite(MethodHandles.collectArguments(printArgs, type)); } }
Думаю, не лишним будет добавить, что данный код является хорошим примером использования динамических методов, но не желателен к использованию в продуктивных решениях.
Еще одна возможность использования InvokeDynamic заключается в том, что мы можем объявлять переменные данного типа. Пример:
InvokeDynamic str = "main"; InvokeDynamic date = new Date();
All in all
Думаю, основные моменты нововведений динамики в Java 7 мы рассмотрели. Как мне кажется, в большинстве своем пакет java.dyn будет полезен не для постоянного использования в продуктивном java коде (хотя, кому как нравится), а именно для долгожданной поддержки разработки динамических языков на основе байт-кода, таких как Ruby, Python.
Как видите в работе с Java есть много нюансов, которые нужно знать. И если вас интересуют все нюансы работы с этой и более новыми версиями Java, переходите по ссылке https://beauty-of-java.site. На сайте есть множество статей, которые подойдут как для новичков, так и для профессиональных программистов.