Що називаєтьсядинамічним і як я ним користуюся?


159

Я постійно чую про всі нові цікаві функції, які додаються до JVM, і одна з цих цікавих функцій викликається динамікою. Мені хотілося б знати, що це таке, і як це полегшує чи якісніше відображає програмування на Java?

Відповіді:


165

Це нова інструкція JVM, яка дозволяє компілятору генерувати код, який викликає методи з більш певною специфікацією, ніж це було раніше, якщо ви знаєте, що таке " набирання качки ", виклик динаміки в основному дозволяє вводити качок. Ви, як програміст Java, не так вже й багато чого можете зробити з цим; якщо ви творець інструментів, ви можете використовувати його для створення більш гнучких та ефективних мов на основі JVM. Ось справді солодкий пост у блозі, який дає багато деталей.


3
У щоденному програмуванні Java не рідкість бачити відображення, яке використовується для динамічного виклику методів meth.invoke(args). То як же invokedynamicвписується meth.invoke?
Девід К.

1
Повідомлення в блозі, про MethodHandleяке я згадую, розповідає про те , що це справді така ж річ, але з значно більшою гнучкістю. Але реальна сила у всьому цьому полягає не в доповненнях до мови Java, а в можливостях самої JVM у підтримці інших мов, які по суті є більш динамічними.
Ернест Фрідман-Хілл


1
Здається, що Java 8 перекладає деякі з лямбдахів, використовуючи invokedynamicякі робить його виконавцями (порівняно з загортанням їх в анонімний внутрішній клас, який був майже єдиним вибором перед представленням invokedynamic). Швидше за все, багато функціональних мов програмування поверх JVM вирішать компілювати це замість анон-внутрішніх класів.
Надер Ганбарі

2
Лише невелике попередження про те, що повідомлення в блозі з 2008 року є безнадійно застарілим і не відображає реального стану випуску (2011).
Холгер

9

Деякий час тому 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своїми програмами.


41
invokedynamicніколи не передбачалося використовувати для програмістів Java. IMO це зовсім не відповідає філософії Java. Він був доданий як функція JVM для мов, які не є Java.
Марк Петерс

5
@ Marark Хто ніколи не замислювався? Це не так, як у знаменитостей мови Java чітка структура влади, або є чітко визначений колективний "намір". Що стосується філософії мови - це цілком можливо, див. Пояснення Ніла Гафтера (зрадника!): Infoq.com/presentations/Statically-Dynamic-Typing-Neal-Gafter
незаперечне

3
@mark peters: invokedynamic насправді також призначений для програмістів Java, не доступних безпосередньо. Це основа для закриття Java 8.
M Platvoet

2
@irreputable: Ніколи не передбачалося вкладачами JSR. Це говорить про те, що назва JSR - "Підтримка динамічно набраних мов на платформі Java". Java не є динамічно набраною мовою.
Марк Петерс

5
@M Platvoet: Я не був в курсі закриттів, але це, безумовно, не буде абсолютною вимогою щодо закриття. Іншим варіантом, який вони обговорювали, було просто виготовлення синтаксичного цукру закриття для анонімних внутрішніх класів, що можна зробити без зміни специфікації VM. Але моя думка полягала в тому, що JSR ніколи не мав намір динамічно вводити мову Java, що багато чого зрозуміло, якщо ви читаєте JSR.
Марк Петерс

4

Є два поняття, які слід зрозуміти, перш ніж продовжувати викликати динаміку.

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.


4

Як частина моєї статті 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

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, динаміка виклику була використана для реалізації таких функцій, як:

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.