Ručke metoda u Javi

1. Uvod

U ovom ćemo članku istražiti važan API koji je predstavljen u Javi 7 i poboljšan u sljedećim verzijama, java.lang.invoke.MethodHandles.

Konkretno, naučit ćemo što su ručke metoda, kako ih stvoriti i kako ih koristiti.

2. Što su ručke metoda?

Dolazeći do njegove definicije, kako je navedeno u API dokumentaciji:

Ručka metode je otkucana, izravno izvršna referenca na temeljnu metodu, konstruktor, polje ili sličnu operaciju na niskoj razini, s neobaveznim transformacijama argumenata ili vraćenih vrijednosti.

Na jednostavniji način, ručke metoda su mehanizam niske razine za pronalaženje, prilagođavanje i pozivanje metoda.

Ručke metoda su nepromjenjive i nemaju vidljivo stanje.

Za stvaranje i korištenje a MethodHandle, Potrebna su 4 koraka:

  • Izrada pretraživanja
  • Izrada tipa metode
  • Pronalaženje ručke metode
  • Pozivanje ručke metode

2.1. Metode u odnosu na refleksiju

Uvedene su ručke metoda kako bi se radilo uz postojeće java.lang.reflect API, jer služe u različite svrhe i imaju različite karakteristike.

Sa stajališta izvedbe, MetodaRuke API može biti mnogo brži od Reflection API-ja jer se provjere pristupa vrše u vrijeme izrade, a ne u vrijeme izvršenja. Ova se razlika pojačava ako je prisutan upravitelj sigurnosti, jer su pregledi članova i klasa podložni dodatnim provjerama.

Međutim, s obzirom da izvedba nije jedina mjera prikladnosti za zadatak, također moramo uzeti u obzir da MetodaRuke API je teže koristiti zbog nedostatka mehanizama kao što su nabrajanje klase članova, inspekcija oznaka pristupačnosti i još mnogo toga.

Iako je tako MetodaRuke API nudi mogućnost kariranja metoda, promjene vrsta parametara i promjene njihovog redoslijeda.

Imajući jasnu definiciju i ciljeve MetodaRuke API, sada možemo početi raditi s njima, počevši od pretraživanja.

3. Stvaranje Pogledaj

Prva stvar koju moramo učiniti kada želimo stvoriti ručicu metode je dohvaćanje pretraživanja, tvorničkog objekta koji je odgovoran za stvaranje obrađivača metoda za metode, konstruktore i polja koja su vidljiva klasi pretraživanja.

Kroz MetodaRuke API, moguće je stvoriti objekt pretraživanja s različitim načinima pristupa.

Stvorimo lookup koji omogućuje pristup javnost metode:

MethodHandles.Lookup publicLookup = MethodHandles.publicLookup ();

Međutim, u slučaju da želimo imati pristup i privatni i zaštićen metode, umjesto toga možemo koristiti Pogledaj() metoda:

MethodHandles.Lookup lookup = MethodHandles.lookup ();

4. Stvaranje a MethodType

Da biste mogli stvoriti MethodHandle, objekt pretraživanja zahtijeva definiciju svog tipa i to se postiže pomoću MethodType razred.

Posebno, a MethodType predstavlja argumente i tip povratka koji prihvaća i vraća obrađivač metode ili ga prosljeđuje i očekuje pozivalac obrade metode.

Struktura a MethodType je jednostavan i tvori ga povratni tip zajedno s odgovarajućim brojem tipova parametara koji se moraju pravilno podudarati između ručice metode i svih njezinih pozivatelja.

Na isti način kao MethodHandle, čak i slučajevi a MethodType su nepromjenjive.

Pogledajmo kako je moguće definirati a MethodType koji specificira a java.util.Popis klasa kao povratni tip i an Objekt niz kao vrsta unosa:

MethodType mt = MethodType.methodType (List.class, Object []. Class);

U slučaju da metoda vrati primitivni tip ili poništiti kao njegov povratni tip koristit ćemo klasu koja predstavlja te tipove (void.class, int.class ...).

Definirajmo a MethodType koji vraća int vrijednost i prihvaća Objekt:

MethodType mt = MethodType.methodType (int.class, Object.class);

Sada možemo nastaviti s stvaranjem MethodHandle.

5. Pronalaženje a MethodHandle

Nakon što definiramo tip metode, kako bismo stvorili MethodHandle, moramo ga pronaći putem Pogledaj ili publicLookup objekt, pružajući također izvornu klasu i ime metode.

Tvornica pretraživanja posebno pruža skup metoda koje nam omogućuju pronalaženje ručke metode na odgovarajući način s obzirom na opseg naše metode. Polazeći od najjednostavnijeg scenarija, istražimo glavne.

5.1. Ručka metode za metode

Koristiti findVirtual () metoda omogućuju nam da stvorimo MethodHandle za objektnu metodu. Stvorimo jedan na temelju concat () metoda Niz razred:

MethodType mt = MethodType.methodType (String.class, String.class); MethodHandle concatMH = publicLookup.findVirtual (String.class, "concat", mt);

5.2. Ručka metode za statičke metode

Kada želimo pristupiti statičkoj metodi, umjesto toga možemo koristiti findStatic () metoda:

MethodType mt = MethodType.methodType (List.class, Object []. Class); MethodHandle asListMH = publicLookup.findStatic (Arrays.class, "asList", mt);

U ovom smo slučaju stvorili ručicu metode koja pretvara niz od Predmeti do a Popis od njih.

5.3. Ručka metode za konstruktore

Pristup konstruktoru može se izvršiti pomoću findConstructor () metoda.

Stvorimo metodu koja se ponaša kao konstruktor Cijeli broj razred, prihvaćajući a Niz atribut:

MethodType mt = MethodType.methodType (void.class, String.class); MethodHandle newIntegerMH = publicLookup.findConstructor (Integer.class, mt);

5.4. Ručka metode za polja

Korištenjem ručice metode moguće je dobiti pristup i poljima.

Počnimo definirati Knjiga razred:

javna klasa Book {String id; Naslov niza; // konstruktor}

Imajući kao preduvjet izravnu vidljivost pristupa između ručice metode i deklariranog svojstva, možemo stvoriti ručicu metode koja se ponaša kao geter:

MethodHandle getTitleMH = lookup.findGetter (Book.class, "title", String.class);

Za daljnje informacije o rukovanju varijablama / poljima pogledajte pogled na Demistificirane ručke varijabli Java 9, gdje razgovaramo o API-ju java.lang.invoke.VarHandle, dodanom u Javi 9.

5.5. Ručka metode za privatne metode

Stvaranje ručke metode za privatnu metodu može se izvršiti uz pomoć java.lang.reflect API.

Počnimo dodavati a privatni metoda za Knjiga razred:

private String formatBook () {return id + ">" + title; }

Sada možemo stvoriti ručicu metode koja se ponaša točno kao formatBook () metoda:

Method formatBookMethod = Book.class.getDeclaredMethod ("formatBook"); formatBookMethod.setAccessible (true); MethodHandle formatBookMH = lookup.unreflect (formatBookMethod);

6. Pozivanje ručke metode

Nakon što kreiramo ručke za metode, sljedeći je korak njihova upotreba. Konkretno, MethodHandle klasa pruža 3 različita načina za izvršavanje ručice metode: prizvati (), invokeWithArugments () i invokeExact ().

Počnimo s prizivati opcija.

6.1. Pozivanje ručke metode

Kada koristite prizvati () metodom, prisiljavamo broj argumenata (arity) koji treba popraviti, ali dopuštamo izvođenje lijevanja i boksanja / otpakiranja argumenata i vrsta povratka.

Pogledajmo kako je moguće koristiti prizvati () s uokvirenim argumentom:

MethodType mt = MethodType.methodType (String.class, char.class, char.class); MethodHandle replaceMH = publicLookup.findVirtual (String.class, "replace", mt); Izlaz niza = (Niz) replaceMH.invoke ("jovo", Character.valueOf ('o'), 'a'); assertEquals ("java", izlaz);

U ovom slučaju, zamijeniMH zahtijeva ugljen argumenti, ali prizvati () izvodi raspakiravanje na Lik argument prije njegova izvršenja.

6.2. Pozivanje na argumente

Pozivanje ručke metode pomoću invokeWithArguments metoda, najmanje je restriktivna od tri mogućnosti.

U stvari, omogućuje pozivanje varijabilne arity, uz lijevanje i boksanje / otpakiranje argumenata i vrsta povratka.

Dolazeći na praksu, ovo nam omogućuje stvaranje a Popis od Cijeli broj počevši od niz od int vrijednosti:

MethodType mt = MethodType.methodType (List.class, Object []. Class); MethodHandle asList = publicLookup.findStatic (Arrays.class, "asList", mt); Lista popisa = (Lista) asList.invokeWithArguments (1,2); assertThat (Arrays.asList (1,2), je (popis));

6.3. Pozivanje na točno

U slučaju da želimo biti restriktivniji u načinu na koji izvršavamo hvataljku metode (broj argumenata i njihov tip), moramo koristiti invokeExact () metoda.

Zapravo, ne pruža nikakvo lijevanje za navedenu klasu i zahtijeva fiksni broj argumenata.

Da vidimo kako možemo iznos dva int vrijednosti pomoću ručice metode:

MethodType mt = MethodType.methodType (int.class, int.class, int.class); MethodHandle sumMH = lookup.findStatic (Integer.class, "sum", mt); int zbroj = (int) zbrojMH.invokeExact (1, 11); assertEquals (12, zbroj);

Ako u ovom slučaju odlučimo prijeći na invokeExact metoda broj koji nije int, zaziv će dovesti do WrongMethodTypeException.

7. Rad s nizom

MetodaRuke nisu namijenjeni radu samo s poljima ili objektima, već i s nizovima. Zapravo, s asSpreader () API, moguće je napraviti ručku metode širenja polja.

U ovom slučaju, ručka metode prihvaća argument niza, šireći njegove elemente kao pozicijske argumente, a po želji i duljinu niza.

Pogledajmo kako možemo proširiti ručicu metode kako bismo provjerili jesu li elementi unutar niza jednaki:

MethodType mt = MethodType.methodType (boolean.class, Object.class); MethodHandle jednako = publicLookup.findVirtual (String.class, "jednako", mt); MethodHandle methodHandle = equals.asSpreader (Objekt []. Klasa, 2); assertTrue ((boolean) methodHandle.invoke (novi Object [] {"java", "java"}));

8. Poboljšanje ručke metode

Jednom kada definiramo hvataljku metode, moguće ju je poboljšati vezivanjem ručke metode za argument, a da je zapravo ne pozivamo.

Primjerice, u Javi 9 takvo se ponašanje koristi za optimizaciju Niz spajanje.

Pogledajmo kako možemo izvršiti spajanje, vežući sufiks za naš concatMH:

MethodType mt = MethodType.methodType (String.class, String.class); MethodHandle concatMH = publicLookup.findVirtual (String.class, "concat", mt); MethodHandle bindedConcatMH = concatMH.bindTo ("Pozdrav"); assertEquals ("Pozdrav svijetu!", bindedConcatMH.invoke ("Svijet!"));

9. Java 9 Poboljšanja

S Javom 9 malo je poboljšanja u sustavu MetodaRuke API s ciljem da ga učini puno lakšim za upotrebu.

Poboljšanja su zahvatila 3 glavne teme:

  • Funkcije pretraživanja - omogućavanje pretraživanja klasa iz različitih konteksta i podrška neastraktnim metodama u sučeljima
  • Rukovanje argumentima - poboljšanje presavijanja argumenata, prikupljanja argumenata i širenja argumenata
  • Dodatne kombinacije - dodavanje petlji (petlja, whileLoop, doWhileLoop ...) i bolju podršku za rukovanje iznimkama s probatiKonačno

Te su promjene rezultirale nekoliko dodatnih pogodnosti:

  • Povećane optimizacije JVM kompajlera
  • Smanjenje instantacije
  • Omogućena preciznost u korištenju MetodaRuke API

Pojedinosti o poboljšanjima dostupna su na MetodaRuke API Javadoc.

10. Zaključak

U ovom smo članku obradili MetodaRuke API, što su i kako ih možemo koristiti.

Također smo razgovarali o tome kako se odnosi na Reflection API, a budući da ručke metoda dopuštaju operacije na niskoj razini, trebalo bi izbjegavati njihovo korištenje, osim ako ne odgovaraju u potpunosti opsegu posla.

Kao i uvijek, cjeloviti izvorni kod za ovaj članak dostupan je na Githubu.


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