Ohjelmointi

Käynnistetty dynaaminen 101

Oraclen Java 7 -julkaisu esitteli uuden kutsutaan dynaamiseksi tavukoodikäsky Java-virtuaalikoneelle (JVM) ja uusi java.lang.invoke API-paketti luokan vakiokirjastoon. Tämä viesti esittelee sinut tähän ohjeeseen ja sovellusliittymään.

Mitä ja miten kutsutaan dynaamiseksi

K: Mikä on kutsutaan dynaamiseksi?

A:kutsutaan dynaamiseksi on tavukoodikäsky, joka helpottaa dynaamisten kielten (JVM) käyttöönottoa dynaamisen menetelmän kutsun avulla. Tämä ohje on kuvattu JVM-määrityksen Java SE 7 -versiossa.

Dynaamiset ja staattiset kielet

A dynaaminen kieli (tunnetaan myös nimellä dynaamisesti kirjoitettu kieli) on korkean tason ohjelmointikieli, jonka tyyppitarkistus suoritetaan yleensä ajon aikana, ominaisuus tunnetaan nimellä dynaaminen kirjoittaminen. Tyyppitarkistus varmistaa, että ohjelma on tyyppi turvallinen: kaikilla toimintoargumenteilla on oikea tyyppi. Groovy, Ruby ja JavaScript ovat esimerkkejä dynaamisista kielistä. ( @ groovy.transform.TypeChecked huomautus saa Groovyn kirjoittamaan tarkistuksen kääntöhetkellä.)

Sen sijaan a staattinen kieli (tunnetaan myös nimellä staattisesti kirjoitettu kieli) suorittaa tyypin tarkistuksen kääntöhetkellä, ominaisuus tunnetaan nimellä staattinen kirjoittaminen. Kääntäjä tarkistaa, että ohjelma on tyypiltään oikea, vaikka se saattaa lykätä tietyn tyyppitarkistusta ajonaikaan (think casts ja tarkistuslähetys ohje). Java on esimerkki staattisesta kielestä. Java-kääntäjä käyttää tämän tyyppisiä tietoja vahvasti kirjoitettujen tavukoodien tuottamiseen, jotka JVM voi suorittaa tehokkaasti.

K: Kuinka kutsutaan dynaamiseksi helpottaa kielen dynaamista käyttöönottoa?

A: Dynaamisella kielellä tyyppitarkistus tapahtuu tyypillisesti ajon aikana. Kehittäjien on läpäistävä sopivat tyypit tai riski suorituksenaikaisia ​​epäonnistumisia. Se on usein niin java.lang.objekti on tarkin menetelmäargumentin tyyppi. Tämä tilanne vaikeuttaa tyyppitarkistusta, mikä vaikuttaa suorituskykyyn.

Toinen haaste on, että dynaamiset kielet tarjoavat tyypillisesti mahdollisuuden lisätä kenttiä / menetelmiä ja poistaa niitä olemassa olevista luokista. Seurauksena on, että luokan, menetelmän ja kentän tarkkuutta on lykättävä ajonaikaiseksi. Lisäksi on usein tarpeen mukauttaa menetelmän kutsu kohteeseen, jolla on erilainen allekirjoitus.

Nämä haasteet ovat perinteisesti edellyttäneet tapauskohtaisen ajonaikaisen tuen rakentamista JVM: n päälle. Tämä tuki sisältää käärintätyyppiluokkia, hash-taulukoiden käyttämisen dynaamisen symbolien tarkkuuden aikaansaamiseksi ja niin edelleen. Bytecode luodaan aloituspisteillä ajonaikaan menetelmäpuheluiden muodossa käyttäen mitä tahansa neljästä menetelmäkutsuohjeesta:

  • invokestaattinen käytetään kutsumaan staattinen menetelmiä.
  • invokevirtuaali käytetään kutsumaan julkinen ja suojattu ei-staattinen menetelmiä dynaamisen lähettämisen kautta.
  • invokeinterface on samanlainen kuin invokevirtuaali lukuun ottamatta menetelmälähetystä, joka perustuu rajapintatyyppiin.
  • vetoaa erityiseen käytetään myös esiintymien alustusmenetelmien (konstruktoreiden) käyttämiseen yksityinen nykyisen luokan superluokan menetelmät ja menetelmät.

Tämä ajonaikainen tuki vaikuttaa suorituskykyyn. Luodut tavukoodit vaativat usein useita todellisia JVM-menetelmäkutsuja yhdelle dynaamiselle kielimenetelmän kutsulle. Heijastusta käytetään laajalti ja se heikentää suorituskykyä. Myös monet erilaiset toteutuspolut tekevät JVM: n juuri-in-time (JIT) -kääntäjältä mahdottomaksi optimointien soveltamisen.

Huonon suorituskyvyn korjaamiseksi kutsutaan dynaamiseksi ohjeet poistavat väliaikaisen ajonaikaisen tuen. Sen sijaan ensimmäinen puhelu bootstraps kutsumalla ajonaikaisen logiikan, joka valitsee kohdemenetelmän tehokkaasti, ja seuraavat puhelut kutsuvat tavallisesti kohdemenetelmän tarvitsematta käynnistää uudelleen.

kutsutaan dynaamiseksi hyödyttää myös dynaamisia kielen toteuttajia tukemalla dynaamisesti muuttuvia puhelusivustokohteita - a soita sivustolletarkemmin sanottuna a dynaamisen puhelun sivusto on kutsutaan dynaamiseksi ohje. Lisäksi koska JVM tukee sisäisesti kutsutaan dynaamiseksi, tämä ohje voidaan optimoida paremmin JIT-kääntäjällä.

Menetelmä käsittelee

K: Ymmärrän, että kutsutaan dynaamiseksi toimii metodikahvojen kanssa dynaamisen menetelmän kutsumisen helpottamiseksi. Mikä on menetelmäkahva?

A: A menetelmän kahva on "kirjoitettu, suoraan suoritettava viittaus taustalla olevaan menetelmään, konstruktoriin, kenttään tai vastaavaan matalan tason operaatioon, vaihtoehtoisilla argumenttien tai palautusarvojen muunnoksilla". Toisin sanoen se on samanlainen kuin C-tyylinen funktiosoitin, joka osoittaa suoritettavaa koodia - a kohde - ja joihin voidaan viitata tämän koodin käyttämiseksi. Menetelmän kahvat kuvataan tiivistelmällä java.lang.invoke.MethodHandle luokassa.

K: Voitteko antaa yksinkertaisen esimerkin menetelmän luomisesta ja kutsumisesta?

A: Tutustu luetteloon 1.

Listaus 1. MHD.java (versio 1)

tuo java.lang.invoke.MethodHandle; tuo java.lang.invoke.MethodHandles; tuo java.lang.invoke.MethodType; public class MHD {public static void main (String [] args) heittää Throwable {MethodHandles.Lookup lookup = MethodHandles.lookup (); MethodHandle mh = lookup.findStatic (MHD.luokka, "hei", MethodType.methodType (void.class)); mh.invokeExact (); } static void hello () {System.out.println ("hei"); }}

Luettelossa 1 kuvataan menetelmäkahvan esittelyohjelma, joka koostuu main () ja Hei() luokan menetelmät. Tämän ohjelman tavoitteena on vedota Hei() menetelmäkahvan kautta.

main ()Ensimmäinen tehtävä on hankkia a java.lang.invoke.MethodHandles.Lookup esine. Tämä objekti on tehdas metodikahvojen luomiseen, ja sitä käytetään kohteiden, kuten virtuaalisten menetelmien, staattisten menetelmien, erikoismenetelmien, rakentajien ja kenttäyhteyksien, etsimiseen. Lisäksi se riippuu puhelusivuston kutsukontekstista ja pakottaa menetelmän käsittelyn käyttörajoitukset joka kerta, kun menetelmän käsittely luodaan. Toisin sanoen, puhelusivusto (kuten Listing 1: t main () menetelmä, joka toimii puhelusivustona), joka hankkii hakuobjektin, voi käyttää vain niitä kohteita, jotka ovat puhelusivuston käytettävissä. Hakuobjekti saadaan kutsumalla java.lang.invoke.MethodHandles luokan MethodHandles.Lookup-haku () menetelmä.

publicLookup ()

MethodHandles ilmoittaa myös a MethodHandles.Lookup publicLookup () menetelmä. Toisin kuin Katso ylös(), jota voidaan käyttää menetelmän käsittelemiseen mihin tahansa käytettävissä olevaan menetelmään / rakentajaan tai kenttään, publicLookup () voidaan käyttää menetelmäkahvan hankkimiseen vain julkisesti käytettävissä olevaan kenttään tai julkisesti käytettävissä olevaan menetelmään / rakentajaan.

Saatuaan hakuobjektin tämä objekti on MethodHandle findStatic (luokan viite, merkkijonon nimi, MethodType-tyyppi) - menetelmää kutsutaan menetelmäkahvan saamiseksi Hei() menetelmä. Ensimmäinen argumentti välitettiin findStatic () on viittaus luokkaan (MHD) josta menetelmä (Hei()), ja toinen argumentti on menetelmän nimi. Kolmas argumentti on esimerkki a menetelmän tyyppi, joka "edustaa argumentteja ja palautustyyppiä, jotka metodikahva on hyväksynyt ja palauttanut, tai argumentteja ja palautustyyppiä, jotka metodikahvan soittaja on hyväksynyt ja odottanut". Sitä edustaa java.lang.invoke.MethodType luokka ja saatu (tässä esimerkissä) soittamalla java.lang.invoke.MethodTypeon MethodType methodType (luokan rtype) menetelmä. Tätä menetelmää kutsutaan koska Hei() tarjoaa vain palautustyypin, joka sattuu olemaan mitätön. Tämä palautustyyppi on saatavissa methodType () ohittamalla mitätön. luokka tähän menetelmään.

Palautettu menetelmäkahva on määritetty mh. Tätä objektia käytetään sitten soittamiseen MenetelmäKahvaon Object invokeExact (Object ... argumentit) method, vedota menetelmäkahvaan. Toisin sanoen, invokeExact () johtaa Hei() kutsutaan, ja Hei kirjoitetaan normaaliin ulostulovirtaan. Koska invokeExact () ilmoitetaan heittävän Heitettävä, Olen liittänyt heittää Heitettävä että main () method-otsikko.

K: Edellisessä vastauksessasi mainitsit, että hakuobjekti voi käyttää vain niitä kohteita, joihin puhelusivusto pääsee. Voitteko antaa esimerkin, joka osoittaa, kuinka yritetään hankkia menetelmän kahva saavuttamattomaan kohteeseen?

A: Tutustu luetteloon 2.

Listaus 2. MHD.java (versio 2)

tuo java.lang.invoke.MethodHandle; tuo java.lang.invoke.MethodHandles; tuo java.lang.invoke.MethodType; luokka HW {public void hello1 () {System.out.println ("hello from hello1"); } private void hello2 () {System.out.println ("hei hello2"); }} public class MHD {public static void main (String [] args) heittää Heitettävä {HW hw = new HW (); MethodHandles.Lookup lookup = MethodHandles.lookup (); MethodHandle mh = lookup.findVirtual (HW.luokka, "hei1", MethodType.methodType (void.class)); mh.invoke (hw); mh = lookup.findVirtual (HW.luokka, "hei2", MethodType.methodType (void.luokka)); }}

Listaus 2 julistaa HW (Hei, maailma) ja MHD luokat. HW julistaa a julkinenhei1 () instanssimenetelmä ja a yksityinenhei2 () instanssimenetelmä. MHD julistaa a main () menetelmä, joka yrittää vedota näihin menetelmiin.

main ()Ensimmäinen tehtävä on instantisoida HW valmistautumalla vetoamiseen hei1 () ja hei2 (). Seuraavaksi se hankkii hakuobjektin ja käyttää tätä objektia saadakseen menetelmän kahvan kutsumiseen hei1 (). Tällä kertaa, MethodHandles.Lookupon findVirtual () kutsutaan ja ensimmäinen tähän menetelmään välitetty argumentti on a Luokka objektia kuvaava HW luokassa.

Osoittautuu niin findVirtual () onnistuu, ja sitä seuraavat mh.invoke (hw); lauseke käynnistää hei1 (), johtaen hei hei1 tuotos.

Koska hei1 () On julkinen, se on saatavilla main () method call site. Verrattuna, hei2 () ei ole käytettävissä. Tämän seurauksena toinen findVirtual () kutsu epäonnistuu IllegalAccessException.

Kun suoritat tämän sovelluksen, sinun on noudatettava seuraavaa tulosta:

hello from hello1 Poikkeus säikeessä "main" java.lang.IllegalAccessException: jäsen on yksityinen: HW.hello2 () void, MHD: stä osoitteessa java.lang.invoke.MemberName.makeAccessException (MemberName.java:507) java.lang. invoke.MethodHandles $ Lookup.checkAccess (MethodHandles.java:1172) osoitteessa java.lang.invoke.MethodHandles $ Lookup.checkMethod (MethodHandles.java:1152) osoitteessa java.lang.invoke.MethodHandles $ Lookup.accessVirtual (Method: 648) osoitteessa java.lang.invoke.MethodHandles $ Lookup.findVirtual (MethodHandles.java:641) osoitteessa MHD.main (MHD.java:27)

K: Luettelot 1 ja 2 käyttävät invokeExact () ja vedota() menetelmiä suorittamaan menetelmäkahva. Mitä eroa on näiden menetelmien välillä?

A: Siitä huolimatta invokeExact () ja vedota() on suunniteltu suorittamaan metodikahva (itse asiassa kohdekoodi, johon metodikahva viittaa), ne eroavat toisistaan, kun argumenteille ja palautusarvolle suoritetaan tyyppimuunnoksia. invokeExact () ei suorita automaattista yhteensopivan tyypin muunnosta argumenteissa. Sen argumenttien (tai argumenttilausekkeiden) on oltava täsmälliset tyypin vastaavuudet menetelmän allekirjoituksen kanssa, kukin argumentti on annettava erikseen, tai kaikki argumentit on toimitettava yhdessä matriisina. vedota() vaatii, että sen argumenttien (tai argumenttilausekkeiden) on oltava yhteensopivia tyypin kanssa menetelmän allekirjoituksen kanssa - automaattiset tyyppimuunnokset suoritetaan siten, että kukin argumentti annetaan erikseen tai kaikki argumentit annetaan yhdessä matriisina.

K: Voitteko antaa minulle esimerkin, joka osoittaa kuinka kutsua esiintymäkentän getter ja setter?

A: Tutustu luetteloon 3.

Listaus 3. MHD.java (versio 3)

tuo java.lang.invoke.MethodHandle; tuo java.lang.invoke.MethodHandles; tuo java.lang.invoke.MethodType; luokka Piste {int x; int y; } public class MHD {public static void main (String [] args) heittää Throwable {MethodHandles.Lookup lookup = MethodHandles.lookup (); Pistepiste = uusi piste (); // Aseta x- ja y-kentät. MethodHandle mh = lookup.findSetter (kohta.luokka, "x", sis. Luokka); mh. kutsu (kohta 15); mh = lookup.findSetter (kohta.luokka, "y", sis. luokka); mh. kutsu (kohta 30); mh = lookup.findGetter (kohta.luokka, "x", sis. luokka); int x = (int) mh. kutsu (piste); System.out.printf ("x =% d% n", x); mh = lookup.findGetter (kohta.luokka, "y", sis. luokka); int y = (int) mh. kutsu (piste); System.out.printf ("y =% d% n", y); }}

Luettelossa 3 esitellään a Kohta luokka, jossa on pari 32-bittistä kokonaislukukenttää x ja y. Jokaisen kentän setteriin ja getteriin pääsee soittamalla MethodHandles.Lookupon findSetter () ja findGetter () menetelmiä ja niistä johtuvia MenetelmäKahva palautetaan. Jokainen findSetter () ja findGetter () vaatii a Luokka argumentti, joka tunnistaa kentän luokan, kentän nimen ja a Luokka objekti, joka tunnistaa kentän allekirjoituksen.

vedota() menetelmää käytetään setterin tai getterin suorittamiseen - kulissien takana esiintymäkenttiin pääsee JVM: n kautta putfield ja getfield ohjeet. Tämä menetelmä edellyttää, että viittaus objektiin, jonka kenttää käytetään, välitetään alkuperäisenä argumenttina. Setter-kutsuja varten on lähetettävä myös toinen argumentti, joka koostuu kentälle määritetystä arvosta.

Kun suoritat tämän sovelluksen, sinun on noudatettava seuraavaa tulosta:

x = 15 y = 30

K: Metodikahvan määrittelysi sisältää lauseen "argumenttien tai palautusarvojen valinnaisilla muunnoksilla". Voitteko antaa esimerkin argumentin muunnoksesta?

A: Olen luonut esimerkin Matematiikka luokan kaksoisvoima (kaksinkertainen a, kaksinkertainen b) luokan menetelmä. Tässä esimerkissä saan menetelmän kahvan Pow () ja muuntaa tämän menetelmän kahva siten, että toinen argumentti välitettiin Pow () on aina 10. Tutustu luetteloon 4.