Я продолжаю слышать обо всех новых интересных функциях, которые добавляются в JVM, и одна из этих интересных функций - динамическая. Я хотел бы знать, что это такое и как это делает рефлексивное программирование на Java легче или лучше?
java
reflection
jvm
invokedynamic
Дэвид К.
источник
источник
meth.invoke(args)
. Итак, как этоinvokedynamic
сочетается сmeth.invoke
?MethodHandle
котором я упоминаю, говорит о том , что это действительно то же самое, но с гораздо большей гибкостью. Но реальная сила во всем этом заключается не в дополнениях к языку Java, а в возможностях самой JVM в поддержке других языков, которые по своей природе более динамичны.invokedynamic
что делает их производительными (по сравнению с обертыванием их в анонимный внутренний класс, который был почти единственным выбором перед введениемinvokedynamic
). Скорее всего, многие функциональные языки программирования поверх JVM предпочтут компилировать это вместо anon-inner-classes.Некоторое время назад в C # добавлена классная функция, динамический синтаксис в C #
Думайте об этом как о синтаксическом сахаре для рефлексивных вызовов методов. У него могут быть очень интересные приложения. см. http://www.infoq.com/presentations/Statically-Dynamic-Typing-Neal-Gafter
Нил Гафтер, ответственный за динамический тип C #, только что перешел с SUN на MS. Так что не исключено, что в SUN обсуждались те же самые вещи.
Я помню вскоре после этого, какой-то Java чувак объявил что-то подобное
К сожалению, эта функция отсутствует в Java 7. Очень разочарован. Для программистов на Java у них нет простого способа воспользоваться преимуществами
invokedynamic
своих программ.источник
invokedynamic
никогда не предназначался для программистов на Java. ИМО, это совсем не соответствует философии Java. Он был добавлен как функция JVM для не-Java языков.Есть две концепции, которые нужно понять, прежде чем продолжать вызывать динамику.
1. Статическая и динамическая типизация
Static - проверка типов преформ во время компиляции (например, Java)
Динамический - проверка типов преформ во время выполнения (например, JavaScript)
Проверка типа - это процесс проверки того, что программа безопасна по типу, то есть проверка типизированной информации для переменных класса и экземпляра, параметров метода, возвращаемых значений и других переменных. Например, Java знает о int, String, .. во время компиляции, в то время как тип объекта в JavaScript может быть определен только во время выполнения
2. Сильный против слабого набора текста
Сильный - указывает ограничения на типы значений, предоставляемых его операциям (например, Java)
Слабый - преобразует (преобразует) аргументы операции, если эти аргументы имеют несовместимые типы (например, Visual Basic)
Зная, что Java является статически и слабо типизированным, как вы реализуете динамически и строго типизированные языки в JVM?
Invokedynamic реализует систему времени выполнения, которая может выбрать наиболее подходящую реализацию метода или функции - после компиляции программы.
Пример: имея (a + b) и ничего не зная о переменных a, b во время компиляции, активизировал динамическую привязку этой операции к наиболее подходящему методу в Java во время выполнения. Например, если выясняется, что a, b - строки, вызовите метод (String a, String b). Если выясняется, что a, b являются целыми числами, вызовите метод (int a, int b).
invokedynamic был представлен в Java 7.
источник
Как часть моей статьи Java Records , я сформулировал мотивацию Inoke Dynamic. Давайте начнем с грубого определения Инди.
Представляем Indy
Invoke Dynamic (также известный как Indy ) был частью JSR 292, намереваясь улучшить поддержку JVM для языков динамического типа. После первого выпуска в Java 7
invokedynamic
код операции и егоjava.lang.invoke
багаж довольно широко используются динамическими языками на основе JVM, такими как JRuby.Хотя indy специально разработан для улучшения поддержки динамического языка, он предлагает гораздо больше. Фактически, это подходит для использования там, где разработчик языка нуждается в любой форме динамичности, от акробатики динамического типа до динамических стратегий!
Например, лямбда-выражения Java 8 фактически реализованы с использованием языка
invokedynamic
Java, хотя это статически типизированный язык!Определяемый пользователем байт-код
В течение некоторого времени JVM поддерживала четыре типа
invokestatic
вызова методов : вызывать статические методы,invokeinterface
вызывать методы интерфейса,invokespecial
вызывать конструкторыsuper()
или частные методы иinvokevirtual
вызывать методы экземпляра.Несмотря на различия, эти типы вызовов имеют одну общую черту: мы не можем обогатить их своей собственной логикой . Напротив,
invokedynamic
позволяет нам загружать процесс вызова любым способом, каким мы захотим. Затем JVM позаботится о непосредственном вызове метода Bootstrapped.Как работает инди?
Когда JVM впервые видит
invokedynamic
инструкцию, она вызывает специальный статический метод Bootstrap Method . Метод начальной загрузки - это фрагмент кода Java, который мы написали для подготовки фактической логики, которая должна быть вызвана:Затем метод начальной загрузки возвращает экземпляр
java.lang.invoke.CallSite
. ЭтоCallSite
держит ссылку на фактический метод, то естьMethodHandle
.С этого момента каждый раз, когда JVM
invokedynamic
снова видит эту инструкцию, она пропускает медленный путь и напрямую вызывает основной исполняемый файл. JVM продолжает пропускать медленный путь, если что-то не меняется.Пример: Java 14 Records
Java 14
Records
предоставляет хороший компактный синтаксис для объявления классов, которые должны быть глупыми держателями данных.Учитывая эту простую запись:
Байт-код для этого примера будет выглядеть примерно так:
В таблице методов начальной загрузки :
Таким образом, вызывается метод начальной загрузки для Records,
bootstrap
который находится вjava.lang.runtime.ObjectMethods
классе. Как видите, этот метод начальной загрузки ожидает следующие параметры:MethodHandles.Lookup
представления контекста поиска (Ljava/lang/invoke/MethodHandles$Lookup
часть).toString
,equals
,hashCode
и т.д.) начальная загрузка будет ссылка. Например, когда значение равноtoString
, bootstrap вернетConstantCallSite
(a,CallSite
который никогда не изменяется), которое указывает на фактическуюtoString
реализацию для этой конкретной Записи.TypeDescriptor
метода (Ljava/lang/invoke/TypeDescriptor
часть).Class<?>
Представляющий тип класса Record. ЭтоClass<Range>
в этом случае.min;max
.MethodHandle
на компонент. Таким образом, метод начальной загрузки может создать наMethodHandle
основе компонентов для этой конкретной реализации метода.invokedynamic
Инструкция передает все эти аргументы метода начальной загрузки. Метод Bootstrap, в свою очередь, возвращает экземплярConstantCallSite
. ЭтоConstantCallSite
содержит ссылку на запрошенную реализацию метода, напримерtoString
.Почему Инди?
В отличие от API-интерфейсов Reflection, API-
java.lang.invoke
интерфейс достаточно эффективен, поскольку JVM может полностью видеть все вызовы. Поэтому JVM может применять все виды оптимизаций, пока мы максимально избегаем медленного пути!В дополнение к аргументу эффективности,
invokedynamic
подход более надежен и менее хрупок из-за своей простоты .Более того, сгенерированный байт-код для записей Java не зависит от количества свойств. Таким образом, меньше байт-кода и более быстрое время запуска.
Наконец, давайте предположим, что новая версия Java включает новую и более эффективную реализацию метода начальной загрузки. С
invokedynamic
, наше приложение может воспользоваться этим усовершенствованием без перекомпиляции. Таким образом, у нас есть какая-то прямая двоичная совместимость . Кроме того, это динамическая стратегия, о которой мы говорили!Другие примеры
В дополнение к Java Records, динамический вызов был использован для реализации таких функций, как:
LambdaMetafactory
StringConcatFactory
источник