Відповіді:
Це нова інструкція JVM, яка дозволяє компілятору генерувати код, який викликає методи з більш певною специфікацією, ніж це було раніше, якщо ви знаєте, що таке " набирання качки ", виклик динаміки в основному дозволяє вводити качок. Ви, як програміст Java, не так вже й багато чого можете зробити з цим; якщо ви творець інструментів, ви можете використовувати його для створення більш гнучких та ефективних мов на основі JVM. Ось справді солодкий пост у блозі, який дає багато деталей.
MethodHandleяке я згадую, розповідає про те , що це справді така ж річ, але з значно більшою гнучкістю. Але реальна сила у всьому цьому полягає не в доповненнях до мови Java, а в можливостях самої JVM у підтримці інших мов, які по суті є більш динамічними.
invokedynamicякі робить його виконавцями (порівняно з загортанням їх в анонімний внутрішній клас, який був майже єдиним вибором перед представленням invokedynamic). Швидше за все, багато функціональних мов програмування поверх JVM вирішать компілювати це замість анон-внутрішніх класів.
Деякий час тому C # додав класну функцію, динамічний синтаксис у C #
Object obj = ...; // no static type available
dynamic duck = obj;
duck.quack(); // or any method. no compiler checking.
Подумайте про це як синтаксичний цукор для дзеркальних методів. Це може мати дуже цікаві програми. дивіться http://www.infoq.com/presentations/Statically-Dynamic-Typing-Neal-Gafter
Ніл Гафтер, відповідальний за динамічний тип C #, щойно перейшов з SUN на MS. Тож нерозумно думати, що ті самі речі обговорювались і в SUN.
Пам’ятаю, незабаром після цього якийсь чувак на Java оголосив щось подібне
InvokeDynamic duck = obj;
duck.quack();
На жаль, у Java 7. цієї функції немає де її знайти. Дуже розчарований. Для програмістів Java вони не мають простого способу скористатися invokedynamicсвоїми програмами.
invokedynamicніколи не передбачалося використовувати для програмістів Java. IMO це зовсім не відповідає філософії Java. Він був доданий як функція JVM для мов, які не є Java.
Є два поняття, які слід зрозуміти, перш ніж продовжувати викликати динаміку.
1. Статичне проти динамінського набору тексту
Статичний - перевірка типу преформ під час компіляції (наприклад, Java)
Динамічна - перевірка типу заготовки під час виконання (наприклад, JavaScript)
Перевірка типу - це процес перевірки того, що програма є безпечною для типу, тобто перевірка набраної інформації для змінних класу та екземпляра, параметрів методу, повернених значень та інших змінних. Наприклад, Java знає про int, String, .. під час компіляції, тоді як тип об'єкта в JavaScript можна визначити лише під час виконання
2. Сильний проти слабкого набору тексту
Сильний - задає обмеження на типи значень, що надходять до його операцій (наприклад, Java)
Слабкі - перетворює (викидає) аргументи операції, якщо ці аргументи мають несумісні типи (наприклад, Visual Basic)
Знаючи, що Java є статично і слабко набраною, як ви реалізуєте динамічно та сильно набрані мови в JVM?
Викликана динаміка реалізує систему виконання, яка може вибрати найбільш відповідну реалізацію методу чи функції - після того, як програма складена.
Приклад: маючи (a + b) і нічого не знаючи про змінні a, b під час компіляції, викликавдинамічні карти цієї операції на найбільш відповідний метод у Java під час виконання. Наприклад, якщо виходить a, b - це рядки, то метод виклику (String a, String b). Якщо виявляється, a, b є ints, тоді викликуме метод (int a, int b).
в Java 7 було введено invokedynamic.
Як частина моєї статті Java Records , я чітко висловлювався про мотивацію, що стоїть за Inoke Dynamic. Почнемо з приблизного визначення Інді.
Invoke Dynamic (Також відомий як Indy ) був частиною JSR 292, що має намір покращити підтримку JVM для мов динамічного типу. Після першого випуску в Java 7, invokedynamicOpcode разом із java.lang.invokeбагажем досить широко використовуються динамічними мовами на базі JVM, такими як JRuby.
Хоча Indy спеціально розроблена для підвищення динамічної підтримки мови, вона пропонує набагато більше, ніж це. По суті, його підходить використовувати там, де мовному дизайнеру потрібна будь-яка форма динамічності, від акробатики динамічного типу до динамічної стратегії!
Наприклад, Lambda вирази Java 8 реально реалізовані за допомогою invokedynamic, навіть якщо Java є мовою статичного типу!
Досить тривалий час JVM підтримував чотири типи invokestaticвикликів методів : викликати статичні методи, invokeinterfaceвикликати інтерфейсні методи, invokespecialвикликати конструктори super()або приватні методи та викликувати методи invokevirtualекземплярів.
Незважаючи на їх відмінності, ці типи викликів мають одну загальну рису: ми не можемо збагатити їх власною логікою . Навпаки, invokedynamic дозволяє нам завантажувати процес виклику будь-яким способом, який ми хочемо. Тоді JVM піклується про виклик методу Bootstrapped безпосередньо.
Перший раз, коли JVM бачить invokedynamicінструкцію, вона викликає спеціальний статичний метод, який називається метод Bootstrap . Метод завантаження - це фрагмент коду Java, який ми написали для підготовки фактичної логіки:
Потім метод завантаження повертає екземпляр java.lang.invoke.CallSite. Це CallSiteмає посилання на власне метод, тобто MethodHandle.
Відтепер кожен раз, коли JVM invokedynamicзнову бачить цю інструкцію, вона пропускає повільний шлях і безпосередньо викликає базовий виконуваний файл. JVM продовжує пропускати повільний шлях, якщо щось не зміниться.
Java 14 Recordsнадає хороший компактний синтаксис для оголошення класів, які повинні бути тупими власниками даних.
Враховуючи цей простий запис:
public record Range(int min, int max) {}
Байт-код для цього прикладу матиме щось на кшталт:
Compiled from "Range.java"
public java.lang.String toString();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokedynamic #18, 0 // InvokeDynamic #0:toString:(LRange;)Ljava/lang/String;
6: areturn
У таблиці таблиць методів завантаження :
BootstrapMethods:
0: #41 REF_invokeStatic java/lang/runtime/ObjectMethods.bootstrap:
(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;
Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;
Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;
Method arguments:
#8 Range
#48 min;max
#50 REF_getField Range.min:I
#51 REF_getField Range.max:I
Отже, називається метод завантаження для 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Інструкція передає всі ці аргументи методу початкового завантаження. Метод завантаження, у свою чергу, повертає екземпляр ConstantCallSite. Тут ConstantCallSiteміститься посилання на запрошений спосіб застосування, наприклад toString.
На відміну від API відображення, java.lang.invokeAPI є досить ефективним, оскільки JVM може повністю бачити всі виклики. Тому JVM може застосовувати всілякі оптимізації, якщо ми максимально уникаємо повільного шляху!
Окрім аргументу ефективності, invokedynamicпідхід є більш надійним та менш крихким через свою простоту .
Більше того, згенерований байт-код для Java Records не залежить від кількості властивостей. Отже, менше байт-коду та швидший час запуску.
Нарешті, припустимо, що нова версія Java включає нову та більш ефективну реалізацію методу завантаження. З invokedynamicнашим додатком можна скористатися цим удосконаленням без перекомпіляції. Таким чином ми маємо якусь форвардну бінарну сумісність . Крім того, це динамічна стратегія, про яку ми говорили!
Крім Java Records, динаміка виклику була використана для реалізації таких функцій, як:
LambdaMetafactoryStringConcatFactory
meth.invoke(args). То як жеinvokedynamicвписуєтьсяmeth.invoke?