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 vyvolaniestatický
metódy.invokevirtual
sa používa na vyvolanieverejné
achrá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.MethodType
je 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 MethodHandle
je 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.Lookup
je 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.Lookup
je 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.