Programovanie

Invokedynamic 101

Vydanie Oracle Java 7 predstavilo nové invokedynamický inštrukcia bytecode do Java Virtual Machine (JVM) a nová java.lang.invoke Balík API do knižnice štandardných tried. Tento príspevok vás oboznámi s touto inštrukciou a API.

Čo a ako invokedynamické

Otázka: Čo je invokedynamický?

A:invokedynamický je inštrukcia bytecode, ktorá uľahčuje implementáciu dynamických jazykov (pre JVM) prostredníctvom vyvolania dynamických metód. Táto inštrukcia je opísaná v Java SE 7 Edition špecifikácie JVM.

Dynamické a statické jazyky

A dynamický jazyk (tiež známy ako a dynamicky písaný jazyk) je programovací jazyk na vysokej úrovni, ktorého kontrola typu sa zvyčajne vykonáva za behu, funkcia známa ako dynamické písanie. Kontrola typu overuje, či program je typ bezpečný: všetky operačné argumenty majú správny typ. Groovy, Ruby a JavaScript sú príkladmi dynamických jazykov. (The @ groovy.transform.TypeChecked anotácia spôsobí, že Groovy napíše kontrolu v čase kompilácie.)

Naproti tomu a statický jazyk (tiež známy ako a staticky napísaný jazyk) vykonáva kontrolu typu v čase kompilácie, funkciu známu ako statické písanie. Kompilátor overuje, či je program správneho typu, aj keď môže odložiť kontrolu niektorých typov za behu (think casts and the checkcast inštrukcia). Java je príkladom statického jazyka. Kompilátor Java používa tieto informácie o type na vytvorenie silne napísaného bajtkódu, ktorý môže JVM efektívne vykonávať.

Otázka: Ako to robí? invokedynamický uľahčiť dynamickú implementáciu jazyka?

A: V dynamickom jazyku sa kontrola typu zvyčajne vykonáva za behu programu. Vývojári musia vyhovieť príslušným typom alebo riskovať zlyhania za behu. Často je to tak java.lang.Objekt je najpresnejší typ argumentu metódy. Táto situácia komplikuje kontrolu typu, ktorá ovplyvňuje výkon.

Ďalšou výzvou je, že dynamické jazyky zvyčajne ponúkajú možnosť pridávať polia / metódy do existujúcich tried a odstraňovať ich. Vo výsledku je potrebné odložiť spustenie triedy, metódy a rozlíšenia poľa za behu. Často je tiež potrebné prispôsobiť vyvolanie metódy cieľu, ktorý má iný podpis.

Tieto výzvy tradične vyžadovali, aby bola podpora runtime ad hoc postavená na vrchole JVM. Táto podpora zahŕňa triedy typu wrapper, použitie hašovacích tabuliek na zabezpečenie dynamického rozlíšenia symbolov atď. Bytecode je generovaný so vstupnými bodmi do modulu runtime vo forme volaní metód pomocou ktoréhokoľvek zo štyroch pokynov na vyvolanie metódy:

  • invokestatický sa používa na vyvolanie statický metódy.
  • invokevirtual sa používa na vyvolanie verejné a chránené ne-statický metódy prostredníctvom dynamického odosielania.
  • vyvolaťrozhranie je podobný invokevirtual okrem odoslania metódy založenej na type rozhrania.
  • invokálne špeciálne sa používa na vyvolanie inštančných inicializačných metód (konštruktorov) a tiež súkromné metódy a metódy nadtriedy súčasnej triedy.

Táto runtime podpora ovplyvňuje výkon. Generovaný bytecode často vyžaduje niekoľko skutočných vyvolaných metód JVM pre jedno vyvolanie metódy s dynamickým jazykom. Odraz je často používaný a prispieva k zhoršeniu výkonu. Mnoho rôznych spôsobov vykonávania tiež znemožňuje, aby kompilátor JVM just-in-time (JIT) použil optimalizácie.

Na riešenie slabého výkonu, invokedynamický inštrukcia ruší runtime podporu ad hoc. Namiesto toho prvý hovor bootstrapy vyvolaním runtime logiky, ktorá efektívne vyberie cieľovú metódu, a následné volania typicky vyvolajú cieľovú metódu bez nutnosti opätovného zavedenia systému.

invokedynamický prospieva aj implementátorom dynamických jazykov podporou dynamicky sa meniacich cieľov webových stránok volania - a volať web, konkrétnejšie a stránka s dynamickým volaním je invokedynamický poučenie. Ďalej preto, že JVM vnútorne podporuje invokedynamický, túto inštrukciu môže lepšie optimalizovať kompilátor JIT.

Metóda rukoväte

Otázka: Rozumiem tomu invokedynamický pracuje s úchytmi metód na uľahčenie dynamického vyvolania metódy. Čo je to metóda?

A: A metóda zvládnuť je „typizovaný, priamo spustiteľný odkaz na základnú metódu, konštruktor, pole alebo podobnú operáciu na nízkej úrovni s voliteľnými transformáciami argumentov alebo návratových hodnôt.“ Inými slovami, je to podobné ako ukazovateľ funkcie v štýle C, ktorý ukazuje na spustiteľný kód - a cieľ - a na ktoré je možné odvolať odkaz na vyvolanie tohto kódu. Popisy metód sú opísané v abstrakte java.lang.invoke.MethodHandle trieda.

Otázka: Môžete uviesť jednoduchý príklad vytvorenia a vyvolania metódy?

A: Pozrite sa na zoznam 1.

Zoznam 1. MHD.java (verzia 1)

import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; public class MHD {public static void main (String [] args) throws Throwable {MethodHandles.Lookup lookup = MethodHandles.lookup (); MethodHandle mh = lookup.findStatic (MHD.class, "ahoj", MethodType.methodType (void.class)); mh.invokeExact (); } static void ahoj () {System.out.println ("ahoj"); }}

Zoznam 1 popisuje demonštračný program spracovania metódy pozostávajúci z hlavný() a Ahoj() triedne metódy. Cieľom tohto programu je vyvolať Ahoj() cez handle metódy.

hlavný()Prvou úlohou je získať a java.lang.invoke.MethodHandles.Lookup objekt. Tento objekt je továreň na vytváranie popisovačov metód a používa sa na vyhľadávanie cieľov, ako sú virtuálne metódy, statické metódy, špeciálne metódy, konštruktory a poľné prístupové objekty. Ďalej to závisí od kontextu vyvolania stránky volania a vynucuje obmedzenia prístupu k obslužnej metóde zakaždým, keď je obslužná metóda vytvorená. Inými slovami, webová stránka hovoru (napríklad Zoznam 1 hlavný() metóda fungujúca ako hovorová stránka), ktorá získa vyhľadávací objekt, môže pristupovať iba k tým cieľom, ktoré sú prístupné pre hovorovú stránku. Vyhľadávací objekt sa získa vyvolaním súboru java.lang.invoke.MethodHandles triedy MethodHandles.Lookup lookup () metóda.

publicLookup ()

MethodHandles tiež vyhlasuje a MethodHandles.Lookup publicLookup () metóda. Na rozdiel od vyhľadať(), ktoré možno použiť na získanie popisovača metódy pre ľubovoľnú prístupnú metódu / konštruktor alebo pole, publicLookup () možno použiť na získanie popisovača metódy do verejne prístupného poľa alebo iba k verejne prístupnej metóde / konštruktoru.

Po získaní vyhľadávacieho objektu je tento objekt MethodHandle findStatic (trieda refc, názov reťazca, typ MethodType) metóda sa volá na získanie metódy handle na Ahoj() metóda. Prvý argument prešiel na findStatic () je odkaz na triedu (MHD) z ktorého metóda (Ahoj()) a druhým argumentom je názov metódy. Tretím argumentom je príklad a typ metódy, ktorý „predstavuje argumenty a návratový typ prijatý a vrátený popisovačom metódy, alebo argumenty a návratový typ odovzdaný a očakávaný volajúcim popisovača metódy.“ Predstavuje to inštancia súboru java.lang.invoke.MethodType triedy a získali (v tomto príklade) volaním java.lang.invoke.MethodTypeje MethodType methodType (trieda rtype) metóda. Táto metóda sa nazýva preto Ahoj() poskytuje iba návratový typ, ktorý sa stane neplatný. Tento návratový typ je k dispozícii pre methodType () prechodom neplatnosť.trieda k tejto metóde.

Vrátený popisovač metódy je priradený k mh. Tento objekt sa potom použije na volanie MethodHandleje Object invokeExact (Object ... args) metóda, vyvolať popisovač metódy. Inými slovami, invokeExact () výsledky v Ahoj() byť volaný a Ahoj sa zapisuje do štandardného výstupného toku. Pretože invokeExact () je vyhlásený na hod Hoditeľné, Pripojil som hodí Throwable do hlavný() hlavička metódy.

Otázka: Vo svojej predchádzajúcej odpovedi ste sa zmienili, že vyhľadávací objekt má prístup iba k tým cieľom, ktoré sú prístupné na stránku volania. Môžete uviesť príklad, ktorý demonštruje pokus získať metódu handle na neprístupný cieľ?

A: Pozrite sa na zoznam 2.

Zoznam 2. MHD.java (verzia 2)

import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; trieda HW {public void hello1 () {System.out.println ("ahoj z hello1"); } private void hello2 () {System.out.println ("ahoj z hello2"); }} public class MHD {public static void main (String [] args) throws Throwable {HW hw = new HW (); MethodHandles.Lookup lookup = MethodHandles.lookup (); MethodHandle mh = lookup.findVirtual (HW.class, "hello1", MethodType.methodType (void.class)); mh.invoke (hw); mh = lookup.findVirtual (HW.class, "hello2", MethodType.methodType (void.class)); }}

Zoznam 2 vyhlasuje HW (Ahoj, svet) a MHD triedy. HW vyhlasuje a verejnéahoj1 () inštančná metóda a a súkromnéahoj2 () inštančná metóda. MHD vyhlasuje a hlavný() metóda, ktorá sa pokúsi tieto metódy vyvolať.

hlavný()Prvou úlohou je vytvoriť inštanciu HW v rámci prípravy na vyvolanie ahoj1 () a ahoj2 (). Ďalej získa vyhľadávací objekt a pomocou tohto objektu získa popisovač metódy na vyvolanie ahoj1 (). Tentokrát, MethodHandles.Lookupje findVirtual () metóda sa volá a prvý argument odovzdaný tejto metóde je a Trieda objekt popisujúci HW trieda.

Ukazuje sa, že findVirtual () uspeje, a následné mh.invoke (hw); výraz vyvolá ahoj1 (), vyúsťujúce do ahoj od ahoj1 je na výstupe.

Pretože ahoj1 () je verejné, je prístupný pre hlavný() metóda volania stránky. Naproti tomu ahoj2 () nie je prístupný Vo výsledku druhý findVirtual () vyvolanie zlyhá s IllegalAccessException.

Pri spustení tejto aplikácie by ste mali dodržiavať nasledujúci výstup:

ahoj z hello1 Výnimka vo vlákne "main" java.lang.IllegalAccessException: člen je súkromný: HW.hello2 () void, z MHD na java.lang.invoke.MemberName.makeAccessException (MemberName.java:507) na java.lang. invoke.MethodHandles $ Lookup.checkAccess (MethodHandles.java:1172) na java.lang.invoke.MethodHandles $ Lookup.checkMethod (MethodHandles.java:1152) na java.lang.invoke.MethodHandles $ Lookup.accessVirtual: MethodHandles.java 648) na java.lang.invoke.MethodHandles $ Lookup.findVirtual (MethodHandles.java:641) na MHD.main (MHD.java:27)

Otázka: Záznamy 1 a 2 používajú znak invokeExact () a vzývať() metódy na vykonanie metódy. Aký je rozdiel medzi týmito metódami?

A: Hoci invokeExact () a vzývať() sú určené na vykonávanie manipulácie s metódou (v skutočnosti cieľového kódu, na ktorý sa odvoláva obslužná rutina metódy), líšia sa, pokiaľ ide o vykonávanie typových prevodov na argumentoch a návratovej hodnoty. invokeExact () nevykonáva automatickú konverziu kompatibilného typu na argumentoch. Jeho argumenty (alebo výrazy argumentov) musia byť v presnom typovom zhode s podpisom metódy, pričom každý argument je uvedený osobitne, alebo všetky argumenty poskytované spoločne ako pole. vzývať() vyžaduje, aby jeho argumenty (alebo výrazy argumentov) boli typovo kompatibilné s podpisom metódy - uskutočňujú sa automatické konverzie typu, pričom každý argument je poskytovaný osobitne, alebo všetky argumenty poskytované spoločne ako pole.

Otázka: Môžete mi poskytnúť príklad, ktorý ukazuje, ako vyvolať getter a setter poľa inštancie?

A: Pozrite sa na zoznam 3.

Zoznam 3. MHD.java (verzia 3)

import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; trieda Bod {int x; int y; } public class MHD {public static void main (String [] args) throws Throwable {MethodHandles.Lookup lookup = MethodHandles.lookup (); Bodový bod = nový Bod (); // Nastaviť polia x a y. MethodHandle mh = lookup.findSetter (Point.class, "x", int.class); mh.invoke (bod 15); mh = lookup.findSetter (Point.class, "y", int.class); mh.invoke (bod 30); mh = lookup.findGetter (Point.class, "x", int.class); int x = (int) mh.invoke (bod); System.out.printf ("x =% d% n", x); mh = lookup.findGetter (Point.class, "y", int.class); int y = (int) mh.invoke (bod); System.out.printf ("y =% d% n", y); }}

Výpis 3 predstavuje a Bod triedy s dvojicou pomenovaných 32-bitových celočíselných inštančných polí X a r. Nastavovač a vyhľadávač každého poľa je prístupný volaním MethodHandles.Lookupje findSetter () a findGetter () metódy a výsledné MethodHandle sa vracia. Každý z findSetter () a findGetter () vyžaduje a Trieda argument, ktorý identifikuje triedu poľa, názov poľa a a Trieda objekt, ktorý identifikuje podpis poľa.

The vzývať() metóda sa používa na vykonanie setra alebo getra - v zákulisí sa k inštančným poliam pristupuje prostredníctvom JVM Putfield a getfield inštrukcie. Táto metóda vyžaduje, aby sa ako počiatočný argument odovzdal odkaz na objekt, do ktorého poľa sa pristupuje. Pre vyvolávače nastavovača musí byť zadaný aj druhý argument pozostávajúci z hodnoty priradenej k poľu.

Pri spustení tejto aplikácie by ste mali dodržiavať nasledujúci výstup:

x = 15 y = 30

Otázka: Vaša definícia popisovača metódy obsahuje frázu „s voliteľnými transformáciami argumentov alebo návratových hodnôt“. Môžete uviesť príklad transformácie argumentov?

A: Vytvoril som príklad na základe Matematika triedy dvojitý prášok (dvojitý a, dvojitý b) triedna metóda. V tomto príklade získam popisovač metódy na pow () metóda a transformujte túto metódu manipulácie tak, aby bol odovzdaný druhý argument pow () je vždy 10. Pozrite sa na zoznam 4.

$config[zx-auto] not found$config[zx-overlay] not found