„Oracle“ leidimas „Java 7“ pristatė naują iškviestas dinamiškas
baitų kodavimo instrukcija „Java Virtual Machine“ (JVM) ir nauja java.lang.invoke
API paketas į standartinę klasės biblioteką. Šis įrašas supažindina jus su šia instrukcija ir API.
Kas ir kaip iškviesta dinamika
Klausimas: Kas yra iškviestas dinamiškas
?
A:iškviestas dinamiškas
yra baito kodo instrukcija, kuri palengvina dinaminių kalbų (JVM) diegimą naudojant dinaminį metodo iškvietimą. Ši instrukcija aprašyta JVM specifikacijos „Java SE 7“ leidime.
Dinaminės ir statinės kalbos
A dinamiška kalba (taip pat žinomas kaip a dinamiškai spausdinta kalba) yra aukšto lygio programavimo kalba, kurios tipo tikrinimas paprastai atliekamas vykdymo metu, funkcija, žinoma kaip dinaminis spausdinimas. Tipo patikrinimas patikrina, ar programa yra tipo seifas: visi operacijos argumentai turi teisingą tipą. „Groovy“, „Ruby“ ir „JavaScript“ yra dinamiškų kalbų pavyzdžiai. ( @ groovy.transform.TyChecked
anotacija sukelia „Groovy“ tipo patikrinimą kompiliavimo metu.)
Priešingai, a statinė kalba (taip pat žinomas kaip a statiškai įvesta kalba) atlieka tipo patikrinimą kompiliavimo metu, tai funkcija vadinama statinis spausdinimas. Kompiliatorius patikrina, ar programos tipas yra teisingas, nors kai kurių tipų tikrinimą gali atidėti vykdymo laikas („Think Cast“ ir čekis
instrukcija). „Java“ yra statinės kalbos pavyzdys. „Java“ kompiliatorius naudoja šio tipo informaciją, kad sukurtų labai tipizuotą baitų kodą, kurį JVM gali efektyviai vykdyti.
Klausimas: Kaip iškviestas dinamiškas
palengvinti dinamišką kalbos diegimą?
A: Dinamiškoje kalboje tipo tikrinimas paprastai atliekamas vykdymo metu. Kūrėjai turi išlaikyti tinkamus tipus arba rizikuoti vykdymo metu. Dažnai taip būna java.lang.Object
yra tiksliausias metodo argumento tipas. Ši situacija apsunkina tipo patikrinimą, o tai daro įtaką našumui.
Kitas iššūkis yra tas, kad dinaminės kalbos paprastai suteikia galimybę pridėti laukus / metodus ir pašalinti juos iš esamų klasių. Todėl būtina atidėti klasės, metodo ir lauko skiriamąją gebą vykdymui. Be to, dažnai reikia pritaikyti metodo iškvietimą tikslui, turinčiam kitokį parašą.
Dėl šių iššūkių tradiciškai reikėjo ad hoc vykdymo laiko palaikymo sukurti ant JVM. Ši parama apima įvyniojimo tipo klases, maišos lentelių naudojimą dinaminei simbolių raiškai užtikrinti ir pan. „Bytecode“ generuojamas su įvesties taškais į vykdymo laiką metodų iškvietimų forma, naudojant bet kurią iš keturių metodo iškvietimo instrukcijų:
invokestatic
yra naudojamas iškviestistatinis
metodai.invokevirtualus
yra naudojamas iškviestivisuomenės
irsaugomi
nestatinis
metodus per dinaminį išsiuntimą.invokeinterface
yra panašus įinvokevirtualus
išskyrus metodo siuntimą, pagrįstą sąsajos tipu.remiasi specialiuoju
yra naudojamas ir egzempliorių inicijavimo metodams (konstruktoriams), irprivatus
dabartinės klasės superklasės metodai ir metodai.
Ši vykdymo laiko parama turi įtakos našumui. Generuojamam baitkodui dažnai reikia kelių faktinių JVM metodo iškvietimų vienam dinaminio kalbos metodo iškvietimui. Refleksija yra plačiai naudojama ir prisideda prie veiklos pablogėjimo. Be to, dėl daugybės skirtingų vykdymo kelių JVM „just-in-time“ (JIT) kompiliatorius negali pritaikyti optimizavimo.
Siekdama išspręsti blogus rezultatus, iškviestas dinamiškas
instrukcijos panaikina ad hoc vykdymo laiko palaikymą. Vietoj to, pirmasis skambutis bagažinės pasinaudojant vykdymo laiko logika, kuri efektyviai parenka tikslinį metodą, o paskesni skambučiai paprastai iškviečia tikslinį metodą nereikalaujant iš naujo paleisti.
iškviestas dinamiškas
taip pat naudinga dinamiškų kalbų diegėjams, palaikydama dinamiškai besikeičiančius skambučių svetainės tikslus - a skambučio svetainė, tiksliau, a dinaminio skambučio svetainė yra iškviestas dinamiškas
instrukcija. Be to, todėl, kad JVM palaiko viduje iškviestas dinamiškas
, šią instrukciją gali geriau optimizuoti JIT kompiliatorius.
Metodo rankenos
Klausimas: Aš suprantu tai iškviestas dinamiškas
veikia su metodo rankenomis, kad palengvintų dinamišką metodo iškvietimą. Kas yra metodo rankena?
A: A metodo rankena yra „tipinė, tiesiogiai vykdoma nuoroda į pagrindinį metodą, konstruktorių, lauką ar panašią žemo lygio operaciją su pasirinktinėmis argumentų ar grąžinimo verčių transformacijomis“. Kitaip tariant, tai panašu į C stiliaus funkcijų rodyklę, kuri nurodo vykdomąjį kodą - a taikinys - ir kuriai negalima daryti nuorodos į šį kodą. Metodo rankenas apibūdina santrauka java.lang.invoke.MethodHandle
klasė.
Klausimas: Ar galite pateikti paprastą metodo tvarkymo ir iškvietimo pavyzdį?
A: Peržiūrėkite 1 sąrašą.
1 sąrašas. MHD.java
(1 versija)
importuoti java.lang.invoke.MethodHandle; importuoti java.lang.invoke.MethodHandles; importuoti java.lang.invoke.MethodType; public class MHD {public static void main (String [] argumentai) meta Throwable {MethodHandles.Lookup lookup = MethodHandles.lookup (); MethodHandle mh = lookup.findStatic (MHD.class, "labas", MethodType.methodType (void.class)); mh.invokeExact (); } static void labas () {System.out.println ("labas"); }}
1 sąraše aprašoma metodo rankenos demonstravimo programa, susidedanti iš pagrindinis ()
ir Sveiki()
klasės metodai. Šios programos tikslas yra pasinaudoti Sveiki()
per metodo rankeną.
pagrindinis ()
Pirmoji užduotis yra gauti java.lang.invoke.MethodHandles.Lookup
objektas. Šis objektas yra metodas, skirtas rankenoms kurti, ir naudojamas tikslams, tokiems kaip virtualūs metodai, statiniai metodai, specialieji metodai, konstruktoriai ir lauko prieigos priemonės, ieškoti. Be to, tai priklauso nuo skambučių svetainės iškvietimo konteksto ir taiko metodo tvarkymo prieigos apribojimus kiekvieną kartą, kai sukuriama metodo rankena. Kitaip tariant, skambučių svetainė (pvz., 1 sąrašas pagrindinis ()
metodas, veikiantis kaip skambučio svetainė), kuris gauna paieškos objektą, gali pasiekti tik tuos tikslus, kurie yra prieinami skambučio svetainei. Paieškos objektas gaunamas iškviečiant java.lang.invoke.MethodHandles
klasės MethodHandles.Lookup lookup ()
metodas.
publicLookup ()
MetodasRankos
taip pat deklaruoja a MethodHandles.Lookup publicLookup ()
metodas. Skirtingai ieškoti ()
, kuri gali būti naudojama norint gauti metodo rankeną prieinamam metodui / konstruktoriui ar laukui, publicLookup ()
gali būti naudojamas norint gauti metodo rankeną tik viešai prieinamam laukui arba viešai prieinamam metodui / konstruktoriui.
Gavę paieškos objektą, šis objektas „MethodHandle findStatic“ (klasės nuorodos, eilutės pavadinimas, „MethodType“ tipas)
metodas yra vadinamas norint gauti metodo rankeną Sveiki()
metodas. Pirmasis argumentas perduotas „findStatic“ ()
yra nuoroda į klasę (MHD
), iš kurio metodas (Sveiki()
), o antrasis argumentas yra metodo pavadinimas. Trečias argumentas yra a pavyzdys metodo tipas, kuris „reiškia argumentus ir grąžinimo tipą, kurį priėmė ir grąžino metodo rankena, arba argumentus ir grąžinimo tipą, kuriuos perdavė ir tikėjosi metodo rankenos skambintojas“. Tai atstovauja java.lang.invoke.MethodType
klasę, ir gautas (šiame pavyzdyje) paskambinus java.lang.invoke.MethodType
's MethodType methodType (klasės rtype)
metodas. Šis metodas vadinamas todėl Sveiki()
pateikia tik grąžinimo tipą, kuris būna tuštuma
. Šis grąžinimo tipas yra prieinamas methodType ()
praeinant tuštuma.klasė
prie šio metodo.
Grąžinta metodo rankena priskiriama mh
. Šis objektas naudojamas paskambinti MethodHandle
's Object invokeExact (objektas ... argumentai)
metodas, pasinaudoti metodo rankena. Kitaip tariant, invokeExact ()
rezultatai Sveiki()
skambinama ir Sveiki
rašoma į standartinį išvesties srautą. Nes invokeExact ()
deklaruojama mesti Metamas
, Aš pridėjau metimai Metami
į pagrindinis ()
metodo antraštė.
Klausimas: Ankstesniame atsakyme minėjote, kad paieškos objektas gali pasiekti tik tuos tikslus, kurie yra prieinami skambučio svetainei. Ar galite pateikti pavyzdį, parodantį bandymą pasiekti metodo rankeną nepasiekiamam taikiniui?
A: Peržiūrėkite 2 sąrašą.
2 sąrašas. MHD.java
(2 versija)
importuoti java.lang.invoke.MethodHandle; importuoti java.lang.invoke.MethodHandles; importuoti java.lang.invoke.MethodType; klasė HW {public void hello1 () {System.out.println ("labas nuo labo1"); } private void labas2 () {System.out.println ("labas nuo labo2"); }} public class MHD {public static void main (String [] args) metimai Metamas {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 sąrašas skelbia HW
(Sveiki, pasauli) ir MHD
klasės. HW
pareiškia a visuomenės
labas1 ()
egzemplioriaus metodas ir a privatus
labas2 ()
egzemplioriaus metodas. MHD
pareiškia a pagrindinis ()
metodas, kuris bandys pasinaudoti šiais metodais.
pagrindinis ()
Pirmoji užduotis yra akimirksniu HW
rengiantis iškviesti labas1 ()
ir labas2 ()
. Tada jis gauna paieškos objektą ir naudoja šį objektą, kad gautų metodo rankeną iškvietimui labas1 ()
. Šį kartą, MethodHandles.Lookup
's findVirtual ()
metodas yra iškviečiamas ir pirmasis šiam metodui perduotas argumentas yra a Klasė
objektas, apibūdinantis HW
klasė.
Paaiškėjo, kad „findVirtual“ ()
pavyks, ir paskesni mh.invoke (hw);
išraiška sukvies labas1 ()
, kurio rezultatas labas nuo labo1
yra išvestis.
Nes labas1 ()
yra visuomenės
, jis prieinamas pagrindinis ()
metodo skambučio svetainė. Priešingai, labas2 ()
nėra pasiekiamas. Dėl to antrasis „findVirtual“ ()
iškvietimas nepavyks naudojant „IllegalAccessException“
.
Kai paleisite šią programą, turėtumėte stebėti šį išvestį:
labas nuo hello1 Išimtis temoje "main" java.lang.IllegalAccessException: narys yra privatus: HW.hello2 () void, iš MHD adresu java.lang.invoke.MemberName.makeAccessException (MemberName.java:507) java.lang. invoke.MethodHandles $ Lookup.checkAccess (MethodHandles.java:1172) adresu java.lang.invoke.MethodHandles $ Lookup.checkMethod (MethodHandles.java:1152) adresu java.lang.invoke.MethodHandles $ Lookup.accessVirtual (Method: 648) adresu java.lang.invoke.MethodHandles $ Lookup.findVirtual (MethodHandles.java:641) MHD.main (MHD.java:27)
Klausimas: 1 ir 2 sąrašuose naudojamas invokeExact ()
ir iškviesti ()
metodai atlikti metodo rankeną. Kuo skiriasi šie metodai?
A: Nors invokeExact ()
ir iškviesti ()
yra skirti atlikti metodo rankenėlę (iš tikrųjų tikslinį kodą, į kurį nurodo metodo rankena), jie skiriasi, kai reikia atlikti tipo konversijas pagal argumentus ir grąžinimo vertę. invokeExact ()
neatlieka automatinio suderinamo tipo konversijos argumentuose. Jos argumentai (arba argumentų išraiškos) turi tiksliai atitikti metodo parašą, kiekvienas argumentas pateikiamas atskirai arba visi argumentai pateikiami kartu kaip masyvas. iškviesti ()
reikalauja, kad jo argumentai (arba argumentų išraiškos) atitiktų tipą, suderinamą su metodo parašu - atliekamos automatinės tipo konversijos, kiekvienam argumentui pateikiant atskirai arba visiems argumentams kartu pateikiant masyvą.
Klausimas: Ar galite pateikti pavyzdį, kuris parodo, kaip iškviesti egzemplioriaus lauko žymiklį ir seterį?
A: Peržiūrėkite 3 sąrašą.
3 sąrašas. MHD.java
(3 versija)
importuoti java.lang.invoke.MethodHandle; importuoti java.lang.invoke.MethodHandles; importuoti java.lang.invoke.MethodType; klasė Taškas {int x; int y; } public class MHD {public static void main (String [] argumentai) meta Throwable {MethodHandles.Lookup lookup = MethodHandles.lookup (); Taškinis taškas = naujas taškas (); // Nustatykite x ir y laukus. MethodHandle mh = lookup.findSetter (Point.class, "x", int.class); mh.invoke (15 punktas); mh = lookup.findSetter (taškas. klasė, „y“, vid. klasė); mh.invoke (30 punktas); mh = lookup.findGetter (taško klasė, „x“, vidinė klasė); int x = (int) mh. iškvietimas (taškas); System.out.printf ("x =% d% n", x); mh = lookup.findGetter (taškas. klasė, „y“, vid. klasė); int y = (int) mh. iškvietimas (taškas); System.out.printf ("y =% d% n", y); }}
3 sąraše pateikiama a Taškas
klasė su 32 bitų sveikojo skaičiaus egzempliorių laukų pavadinimu x
ir y
. Kiekvieno lauko nustatiklį ir parametrą galima pasiekti paskambinus MethodHandles.Lookup
's „findSetter“ ()
ir „findGetter“ ()
metodus ir gautus MethodHandle
yra grąžinamas. Kiekvienas iš „findSetter“ ()
ir „findGetter“ ()
reikalauja a Klasė
argumentas, nurodantis lauko klasę, lauko pavadinimą ir a Klasė
objektas, identifikuojantis lauko parašą.
iškviesti ()
metodas naudojamas vykdant seterį ar „getter“ - užkulisiuose egzempliorių laukai pasiekiami per JVM putfieldas
ir getfield
instrukcijas. Šis metodas reikalauja, kad nuoroda į objektą, kurio lauką pasiekiama, būtų perduodama kaip pradinis argumentas. Norint nustatyti seterių iškvietimus, taip pat reikia perduoti antrą argumentą, susidedantį iš laukui priskiriamos vertės.
Kai paleisite šią programą, turėtumėte stebėti šį išvestį:
x = 15 y = 30
Klausimas: Metodo rankenos apibrėžime yra frazė „su pasirinktinėmis argumentų arba grąžinimo reikšmių transformacijomis“. Ar galite pateikti argumentų transformavimo pavyzdį?
A: Aš sukūriau pavyzdį, pagrįstą Matematika
klasės dviguba galia (dviguba a, dviguba b)
klasės metodas. Šiame pavyzdyje aš gaunu metodo rankeną Pow ()
metodą ir transformuokite šio metodo rankeną taip, kad antrasis argumentas būtų perduotas Pow ()
yra visada 10
. Peržiūrėkite 4 sąrašą.