Programmēšana

Izsauktais dinamiskais 101

Oracle Java 7 izlaidums ieviesa jaunu izsauktsdinamiski baitkoda instrukcijas Java virtuālajai mašīnai (JVM) un jauna java.lang.invoke API pakete klases klases bibliotēkai. Šis ieraksts iepazīstina jūs ar šo instrukciju un API.

Kas un kā tiek izsaukts par dinamisko

J: Kas ir izsauktsdinamiski?

A:izsauktsdinamiski ir baitkoda instrukcija, kas atvieglo dinamisko valodu ieviešanu (JVM), izmantojot dinamisku metožu izsaukšanu. Šī instrukcija ir aprakstīta JVM specifikācijas Java SE 7 izdevumā.

Dinamiskas un statiskas valodas

A dinamiska valoda (pazīstams arī kā dinamiski rakstīta valoda) ir augsta līmeņa programmēšanas valoda, kuras tipa pārbaude parasti tiek veikta izpildlaika laikā - funkcija, kas pazīstama kā dinamiska rakstīšana. Veicot tipa pārbaudi, tiek pārbaudīta, vai programma ir tipa seifs: visiem darbības argumentiem ir pareizs tips. Groovy, Ruby un JavaScript ir dinamisku valodu piemēri. (The @ groovy.transform.TyChecked anotācija liek Groovijam veikt tipa pārbaudi sastādīšanas laikā.)

Turpretī a statiskā valoda (pazīstams arī kā statiski ierakstīta valoda) veic tipa pārbaudi sastādīšanas laikā, funkciju, kas pazīstama kā statiskā rakstīšana. Sastādītājs pārbauda, ​​vai programmai ir pareizs tips, lai gan tā var atlikt dažu veidu pārbaudi izpildlaika laikā (think casts un čeka raidījums instrukcija). Java ir statiskas valodas piemērs. Java kompilators izmanto šāda veida informāciju, lai izveidotu stingri ierakstītu baitkodu, kuru JVM var efektīvi izpildīt.

J:izsauktsdinamiski veicināt dinamisku valodas ieviešanu?

A: Dinamiskā valodā tipa pārbaude parasti notiek izpildlaikā. Izstrādātājiem jānokārto atbilstoša veida vai jāriskē izpildlaika neveiksmes. Bieži vien tā ir java.lang.Object ir visprecīzākais metodes argumenta tips. Šī situācija sarežģī tipa pārbaudi, kas ietekmē veiktspēju.

Vēl viens izaicinājums ir tāds, ka dinamiskās valodas parasti piedāvā iespēju pievienot laukus / metodes esošajām klasēm un noņemt no tām. Rezultātā ir jāatliek klase, metode un lauka izšķirtspēja uz izpildlaiku. Turklāt bieži ir jāpielāgo metodes izsaukums mērķim, kuram ir atšķirīgs paraksts.

Šīs problēmas tradicionāli prasa, lai JVM tiktu izveidots ad hoc izpildlaika atbalsts. Šis atbalsts ietver iesaiņojuma tipa klases, jaucējgaldu izmantošanu, lai nodrošinātu dinamisku simbolu izšķirtspēju, un tā tālāk. Bytecode tiek ģenerēts ar ieejas punktiem izpildlaika metožu izsaukumu veidā, izmantojot jebkuru no četrām metožu izsaukšanas instrukcijām:

  • invokestātisks tiek izmantots, lai izsauktu statisks metodes.
  • invokevirtuāls tiek izmantots, lai izsauktu publiski un aizsargāts ne-statisks metodes, izmantojot dinamisku nosūtīšanu.
  • invokeinterface ir līdzīgs invokevirtuāls izņemot metodi, kuras nosūtīšana balstās uz saskarnes tipu.
  • izsauc speciālo tiek izmantots, lai izsauktu instances inicializācijas metodes (konstruktorus), kā arī Privāts pašreizējās klases superklases metodes un metodes.

Šis izpildlaika atbalsts ietekmē veiktspēju. Ģenerētajam baitkodam bieži vien ir nepieciešami vairāki faktiskie JVM metožu izsaukumi vienai dinamiskās valodas metodes izsaukšanai. Refleksija tiek plaši izmantota un veicina veiktspējas pasliktināšanos. Turklāt daudzie dažādi izpildes ceļi neļauj JVM tieši laikā (JIT) sastādītājam piemērot optimizāciju.

Lai novērstu sliktu sniegumu, izsauktsdinamiski instrukcija atceļ ad hoc izpildlaika atbalstu. Tā vietā pirmais zvans bootstraps izsaucot izpildlaika loģiku, kas efektīvi atlasa mērķa metodi, un nākamie zvani parasti izsauc mērķa metodi, bez atkārtotas sāknēšanas.

izsauktsdinamiski sniedz labumu arī dinamisko valodu ieviesējiem, atbalstot dinamiski mainīgus zvana vietnes mērķus - a zvana vietne, konkrētāk, a dinamiskā zvana vietne ir izsauktsdinamiski instrukcija. Turklāt tāpēc, ka JVM iekšēji atbalsta izsauktsdinamiski, šo instrukciju var labāk optimizēt JIT kompilators.

Metode rokturi

J: Es to saprotu izsauktsdinamiski darbojas ar metožu rokturiem, lai atvieglotu metožu dinamisku izsaukšanu. Kas ir metodes rokturis?

A: A metode rokturi ir "drukāta, tieši izpildāma atsauce uz pamatā esošo metodi, konstruktoru, lauku vai līdzīgu zema līmeņa darbību ar izvēles argumentu vai atgriešanās vērtību transformācijām". Citiem vārdiem sakot, tas ir līdzīgs C stila funkciju rādītājam, kas norāda uz izpildāmo kodu - a mērķis - un uz kuru var atsaukties, lai izmantotu šo kodu. Metodes rokturus apraksta abstrakts java.lang.invoke.MethodHandle klasē.

J: Vai jūs varat sniegt vienkāršu metožu rokturu izveides un izsaukšanas piemēru?

A: Pārbaudiet 1. sarakstu.

Saraksts 1. MHD.java (1. versija)

importēt java.lang.invoke.MethodHandle; importēt java.lang.invoke.MethodHandles; importēt 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, "sveiki", MethodType.methodType (void.class)); mh.invokeExact (); } static void hello () {System.out.println ("sveiki"); }}

1. sarakstā aprakstīta metodes roktura demonstrēšanas programma, kas sastāv no galvenais () un Sveiki() klases metodes. Šīs programmas mērķis ir atsaukties Sveiki() izmantojot metodes rokturi.

galvenais ()Pirmais uzdevums ir iegūt java.lang.invoke.MethodHandles.Lookup objekts. Šis objekts ir metožu rokturu izveidošanas rūpnīca, un to izmanto, lai meklētu tādus mērķus kā virtuālās metodes, statiskās metodes, īpašās metodes, konstruktori un lauka piekļuves ierīces. Turklāt tas ir atkarīgs no zvana vietnes izsaukuma konteksta un katru reizi, kad tiek izveidots metodes rokturis, tiek ieviesti piekļuves ierobežojumi attiecībā uz metodi. Citiem vārdiem sakot, zvana vietne (piemēram, 1. saraksts galvenais () metode, kas darbojas kā zvana vietne), kas iegūst uzmeklēšanas objektu, var piekļūt tikai tiem mērķiem, kas ir pieejami zvana vietnei. Uzmeklēšanas objekts tiek iegūts, izsaucot java.lang.invoke.MethodHandles klases MethodHandles.Lookup meklēšana () metodi.

publicLookup ()

MethodHandles deklarē arī a MethodHandles.Lookup publicLookup () metodi. Atšķirībā no uzmeklēšana (), ko var izmantot, lai iegūtu metodes rokturi jebkurai pieejamai metodei / konstruktoram vai laukam, publicLookup () var izmantot, lai iegūtu metodes rokturi tikai publiski pieejamam laukam vai publiski pieejamai metodei / konstruktoram.

Pēc uzmeklēšanas objekta iegūšanas šis objekts ir MethodHandle findStatic (klases refc, virknes nosaukums, MethodType tips) metodi sauc, lai iegūtu metodi Sveiki() metodi. Pirmais arguments tika nodots findStatic () ir atsauce uz klasi (MHD), no kuras metode (Sveiki()), un otrais arguments ir metodes nosaukums. Trešais arguments ir a piemērs metodes veids, kas "apzīmē argumentus un atgriešanās tipu, ko pieņēmis un atdevis metožu rokturis, vai argumentus un atgriešanās tipu, ko nodevis un gaidījis metožu apstrādes izsaucējs". To attēlo java.lang.invoke.MethodType klasē, un ieguva (šajā piemērā), zvanot java.lang.invoke.MethodType's MethodType methodType (klases rtype) metodi. Šo metodi sauc par Sveiki() nodrošina tikai atgriešanās veidu, kas notiek spēkā neesošs. Šis atgriešanas veids ir pieejams methodType () paejot garām void.class uz šo metodi.

Atgrieztais metodes rokturis tiek piešķirts mh. Pēc tam šo objektu izmanto, lai piezvanītu MethodHandle's Object invokeExact (Object ... args) metodi, lai izsauktu metodes rokturi. Citiem vārdiem sakot, invokeExact () rezultātā Sveiki() tiek izsaukts, un Sveiki tiek ierakstīts standarta izvades straumē. Tā kā invokeExact () ir deklarēts mest Metams, Es esmu pievienojis metieni Metams uz galvenais () metodes galvene.

J: Iepriekšējā atbildē jūs minējāt, ka uzmeklēšanas objekts var piekļūt tikai tiem mērķiem, kas ir pieejami zvana vietnei. Vai jūs varat sniegt piemēru, kas parāda mēģinājumu iegūt metodes rokturi nepieejamam mērķim?

A: Pārbaudiet 2. sarakstu.

2. saraksts. MHD.java (2. versija)

importēt java.lang.invoke.MethodHandle; importēt java.lang.invoke.MethodHandles; importēt java.lang.invoke.MethodType; klase HW {public void hello1 () {System.out.println ("sveiki no sveiki1"); } private void hello2 () {System.out.println ("sveiki no hello2"); }} public class MHD {public static void main (String [] args) throws Metams {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)); }}

2. saraksts deklarē HW (Sveika, Pasaule) un MHD klases. HW paziņo a publiskilabdien1 () instances metodi un a Privātssveiks2 () instances metode. MHD paziņo a galvenais () metodi, kas mēģinās izmantot šīs metodes.

galvenais ()Pirmais uzdevums ir acumirklī HW gatavojoties izsaukšanai labdien1 () un sveiks2 (). Pēc tam tas iegūst uzmeklēšanas objektu un izmanto šo objektu, lai iegūtu metodes rokturi izsaukšanai labdien1 (). Šoreiz, MethodHandles.Lookup's findVirtual () metode tiek izsaukta, un pirmais šai metodei nodotais arguments ir a Klase objekts, kas apraksta HW klasē.

Izrādās, ka tā findVirtual () izdosies, un nākamie mh.invoke (hw); izteiksme izsauks labdien1 (), kā rezultātā sveiks no sveiki1 tiek izvadīts.

Tā kā labdien1 () ir publiski, tas ir pieejams galvenais () metode zvana vietne. Turpretī labdien2 () nav pieejams. Rezultātā otrais findVirtual () izsaukšana neizdosies ar IllegalAccessException.

Palaidot šo lietojumprogrammu, jums jāievēro šāda izeja:

sveiki no hello1 Izņēmums pavedienā "main" java.lang.IllegalAccessException: member is private: HW.hello2 () void, from MHD at java.lang.invoke.MemberName.makeAccessException (MemberName.java:507) at java.lang. invoke.MethodHandles $ Lookup.checkAccess (MethodHandles.java:1172) vietnē java.lang.invoke.MethodHandles $ Lookup.checkMethod (MethodHandles.java:1152) vietnē java.lang.invoke.MethodHandles $ Lookup.accessVirtual (Method: 648) vietnē java.lang.invoke.MethodHandles $ Lookup.findVirtual (MethodHandles.java:641) vietnē MHD.main (MHD.java:27)

J: 1. un 2. sarakstā tiek izmantots invokeExact () un izsaukt () metodes, lai izpildītu metodi. Kāda ir atšķirība starp šīm metodēm?

A: Lai gan invokeExact () un izsaukt () ir paredzēti, lai izpildītu metodes rokturi (faktiski mērķa kodu, uz kuru atsaucas metodes rokturis), tie atšķiras, veicot tipa pārveidojumus par argumentiem un atgriešanās vērtību. invokeExact () neveic automātisku saderīga tipa pārveidošanu argumentos. Tās argumentiem (vai argumentu izteiksmēm) jābūt precīzai tipa atbilstībai metodes parakstam, katram argumentam jānorāda atsevišķi vai visiem argumentiem kopā kā masīvam. izsaukt () pieprasa, lai tā argumenti (vai argumentu izteiksmes) būtu saderīgi ar tipu ar metodes parakstu - tiek veikti automātiski tipa pārveidojumi, katram argumentam norādot atsevišķi vai visiem argumentiem kopā kā masīvam.

J: Vai varat man sniegt piemēru, kas parāda, kā izsaukt instances lauka noteicēju un iestatītāju?

A: Pārbaudiet 3. sarakstu.

3. saraksts. MHD.java (3. versija)

importēt java.lang.invoke.MethodHandle; importēt java.lang.invoke.MethodHandles; importēt java.lang.invoke.MethodType; klase Punkts {int x; int y; } public class MHD {public static void main (String [] args) throws Throwable {MethodHandles.Lookup lookup = MethodHandles.lookup (); Punktu punkts = jauns Punkts (); // Iestatiet laukus x un y. MethodHandle mh = lookup.findSetter (Point.class, "x", int.class); mh.invoke (15. punkts); mh = lookup.findSetter (Point.class, "y", int.class); mh.invoke (30. punkts); mh = lookup.findGetter (Point.class, "x", int.class); int x = (int) mh. izsaukums (punkts); System.out.printf ("x =% d% n", x); mh = look.findGetter (Point.class, "y", int.class); int y = (int) mh. izsaukums (punkts); System.out.printf ("y =% d% n", y); }}

3. uzskaitījums ievieš a Punkts klase ar pāris 32 bitu vesela skaitļa instances lauku nosaukumiem x un y. Katra lauka iestatītājam un iestatītājam var piekļūt, zvanot MethodHandles.Lookup's findSetter () un findGetter () metodes un no tā izrietošie MethodHandle tiek atgriezta. Katrs no findSetter () un findGetter () prasa a Klase arguments, kas identificē lauka klasi, lauka nosaukumu un a Klase objekts, kas identificē lauka parakstu.

The izsaukt () metodi izmanto, lai izpildītu seteri vai getteru - aizkulisēs instances laukiem var piekļūt, izmantojot JVM putfīlds un getfield instrukcijas. Šī metode prasa, lai kā sākotnējais arguments tiktu nodota atsauce uz objektu, kura laukam tiek piekļūts. Lai iestatītu izsaucējus, jānodod arī otrais arguments, kas sastāv no laukam piešķirtās vērtības.

Palaidot šo lietojumprogrammu, jums jāievēro šāda izeja:

x = 15 y = 30

J: Metodes roktura definīcijā ir ietverta frāze "ar izvēles argumentu vai atgriešanās vērtību transformācijām". Vai jūs varat sniegt argumentu pārveidošanas piemēru?

A: Esmu izveidojis piemēru, pamatojoties uz Matemātika klases dubultā vara (dubultā a, dubultā b) klases metode. Šajā piemērā es iegūstu metodes rokturi pow () metodi, un pārveidojiet šīs metodes rokturi tā, lai otrais arguments tiktu nodots pow () ir vienmēr 10. Pārbaudiet 4. sarakstu.

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