Відповіді:
Це нова інструкція 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, invokedynamic
Opcode разом із 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.invoke
API є досить ефективним, оскільки JVM може повністю бачити всі виклики. Тому JVM може застосовувати всілякі оптимізації, якщо ми максимально уникаємо повільного шляху!
Окрім аргументу ефективності, invokedynamic
підхід є більш надійним та менш крихким через свою простоту .
Більше того, згенерований байт-код для Java Records не залежить від кількості властивостей. Отже, менше байт-коду та швидший час запуску.
Нарешті, припустимо, що нова версія Java включає нову та більш ефективну реалізацію методу завантаження. З invokedynamic
нашим додатком можна скористатися цим удосконаленням без перекомпіляції. Таким чином ми маємо якусь форвардну бінарну сумісність . Крім того, це динамічна стратегія, про яку ми говорили!
Крім Java Records, динаміка виклику була використана для реалізації таких функцій, як:
LambdaMetafactory
StringConcatFactory
meth.invoke(args)
. То як жеinvokedynamic
вписуєтьсяmeth.invoke
?