Metódové popisovače v Jave

1. Úvod

V tomto článku sa budeme venovať dôležitému API, ktoré bolo predstavené v prostredí Java 7 a vylepšené v nasledujúcich verziách, java.lang.invoke.MethodHandles.

Dozvieme sa najmä, čo sú popisovače metód, ako ich vytvárať a ako ich používať.

2. Čo sú to rukoväte metód?

Prichádza k svojej definícii, ako je uvedené v dokumentácii API:

Popisovač metódy 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.

Jednoduchším spôsobom popisovače metód sú nízkoúrovňový mechanizmus na hľadanie, prispôsobovanie a vyvolávanie metód.

Rukoväte metódy sú nemenné a nemajú viditeľný stav.

Na vytvorenie a použitie a MethodHandle, Sú potrebné 4 kroky:

  • Vytvára sa vyhľadávanie
  • Vytvára sa typ metódy
  • Nájdenie popisovača metódy
  • Vyvolanie rukoväte metódy

2.1. Metóda rukoväte vs reflexia

Boli zavedené úchyty metód, aby fungovali súčasne s existujúcimi java.lang.reflect API, pretože slúžia rôznym účelom a majú odlišné vlastnosti.

Z výkonnostného hľadiska MethodHandles API môže byť oveľa rýchlejšie ako Reflection API, pretože kontroly prístupu sa vykonávajú skôr v čase vytvorenia ako v čase vykonania. Tento rozdiel sa zosilní, ak je prítomný bezpečnostný manažér, pretože vyhľadávanie členov a tried je predmetom ďalších kontrol.

Avšak vzhľadom na to, že výkon nie je jediným meradlom vhodnosti pre danú úlohu, musíme tiež zvážiť, že: MethodHandles Rozhranie API sa ťažšie používa z dôvodu nedostatku mechanizmov, ako je výčet tried členov, kontrola príznakov prístupnosti a ďalšie.

Aj napriek tomu MethodHandles API ponúka možnosť curry metód, meniť typy parametrov a meniť ich poradie.

Mať jasnú definíciu a ciele MethodHandles API, teraz s nimi môžeme začať pracovať, a to od vyhľadávania.

3. Vytvorenie Vyhľadať

Prvá vec, ktorú musíme urobiť, keď chceme vytvoriť popisovač metódy, je načítanie vyhľadávania, továrenského objektu, ktorý je zodpovedný za vytváranie popisovačov metód pre metódy, konštruktory a polia, ktoré sú viditeľné pre vyhľadávaciu triedu.

Cez MethodHandles API, je možné vytvoriť vyhľadávací objekt s rôznymi režimami prístupu.

Vytvorme vyhľadávanie, ktoré poskytuje prístup k verejné metódy:

MethodHandles.Lookup publicLookup = MethodHandles.publicLookup ();

Avšak v prípade, že chceme mať prístup aj k súkromné a chránené metódy, môžeme namiesto toho použiť vyhľadať() metóda:

MethodHandles.Lookup lookup = MethodHandles.lookup ();

4. Vytvorenie a MethodType

Aby bolo možné vytvoriť MethodHandle, vyhľadávací objekt vyžaduje definíciu svojho typu a je to dosiahnuté prostredníctvom MethodType trieda.

Najmä a MethodType predstavuje argumenty a návratový typ prijatý a vrátený popisovačom metódy alebo odovzdaný a očakávaný volajúcim popisovača metódy.

Štruktúra a MethodType je jednoduchý a je tvorený návratovým typom spolu s príslušným počtom typov parametrov, ktoré musia byť správne porovnané medzi popisovačom metódy a všetkými jej volajúcimi.

Rovnakým spôsobom ako MethodHandle, dokonca aj prípady a MethodType sú nemenné.

Pozrime sa, ako je možné definovať a MethodType ktorá špecifikuje a java.util.List trieda ako návratový typ a Objekt pole ako typ vstupu:

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

V prípade, že metóda vráti primitívny typ alebo neplatný ako návratový typ použijeme triedu predstavujúcu tieto typy (void.class, int.class ...).

Definujme a MethodType ktorý vráti hodnotu int a prijme Objekt:

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

Teraz môžeme pokračovať v vytváraní MethodHandle.

5. Nájdenie a MethodHandle

Keď sme definovali náš typ metódy, aby sme vytvorili a MethodHandle, musíme to nájsť cez vyhľadať alebo publicLookup objekt, poskytujúci tiež triedu pôvodu a názov metódy.

Vyhľadávacia továreň poskytuje najmä súbor metód, ktoré nám umožňujú nájsť popisovač metódy vhodným spôsobom vzhľadom na rozsah našej metódy. Počnúc najjednoduchším scenárom, poďme preskúmať tie hlavné.

5.1. Metóda pre metódy

Pomocou findVirtual () metóda nám umožňuje vytvoriť MethodHandle pre objektovú metódu. Vytvorme jeden na základe concat () metóda String trieda:

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

5.2. Metóda pre statické metódy

Ak chceme získať prístup k statickej metóde, môžeme namiesto toho použiť findStatic () metóda:

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

V tomto prípade sme vytvorili popisovač metódy, ktorý prevádza pole Predmety do a Zoznam z nich.

5.3. Metóda rukoväte pre konštruktérov

Získanie prístupu ku konštruktoru je možné vykonať pomocou findConstructor () metóda.

Poďme vytvoriť úchyty metód, ktoré sa správajú ako konštruktor súboru Celé číslo triedy, prijíma a String atribút:

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

5.4. Metóda rukoväte pre polia

Pomocou úchytky metódy je možné získať prístup aj k poliam.

Začnime definovať Kniha trieda:

verejná trieda Book {String id; Názov reťazca; // konštruktor}

Ak máme ako podmienku viditeľnosť priameho prístupu medzi popisovačom metódy a deklarovanou vlastnosťou, môžeme vytvoriť popisovač metódy, ktorý sa správa ako getter:

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

Ďalšie informácie o manipulácii s premennými / poľami nájdete v dokumente Java 9 Variable Handles Demystified, kde diskutujeme o rozhraní java.lang.invoke.VarHandle API pridanom v prostredí Java 9.

5.5. Metóda pre súkromné ​​metódy

Vytvorenie popisovača metódy pre súkromnú metódu je možné vykonať pomocou nástroja java.lang.reflect API.

Začnime pridávať a súkromné metóda do Kniha trieda:

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

Teraz môžeme vytvoriť popisovač metódy, ktorý sa správa presne ako formatBook () metóda:

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

6. Vyvolanie rukoväti metódy

Keď sme vytvorili naše popisy metód, ďalším krokom je ich použitie. Najmä MethodHandle trieda poskytuje 3 rôzne spôsoby vykonávania manipulácie s metódou: vzývať(), invokeWithArucements () a invokeExact ().

Začnime s vzývať možnosť.

6.1. Vyvolanie metódy

Pri použití vzývať() Metóda, vynútime počet argumentov (arity), ktoré sa majú opraviť, ale umožňujeme vykonávanie casting a box / unboxing argumentov a návratových typov.

Pozrime sa, ako je možné používať vzývať() s orámovaným argumentom:

MethodType mt = MethodType.methodType (String.class, char.class, char.class); MethodHandle replaceMH = publicLookup.findVirtual (String.class, "replace", mt); Reťazcový výstup = (Reťazec) replaceMH.invoke ("jovo", Character.valueOf ('o'), 'a'); assertEquals ("java", výstup);

V takom prípade nahradiťMH vyžaduje char argumenty, ale vzývať() vykonáva unboxing na Postava argument pred jeho vykonaním.

6.2. Vyvolávanie s argumentmi

Vyvolanie popisovača metódy pomocou invokeWithArguments metóda je najmenej obmedzujúca z týchto troch možností.

V skutočnosti umožňuje variabilné vyvolanie arity, okrem castingu a boxu / unboxingu argumentov a návratových typov.

Keď si to vyskúšame, umožní nám to vytvoriť a Zoznam z Celé číslo počnúc od pole z int hodnoty:

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

6.3. Presné vyvolanie

Ak chceme byť pri spôsobe vykonávania handle metódy (počet argumentov a ich typ) reštriktívnejší, musíme použiť invokeExact () metóda.

V skutočnosti neposkytuje poskytnutú triedu žiadny casting a vyžaduje pevný počet argumentov.

Pozrime sa, ako môžeme súčet dva int hodnoty pomocou handle metódy:

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

Ak sa v takom prípade rozhodneme prejsť na invokeExact metóda číslo, ktoré nie je int, bude vyvolanie viesť k WrongMethodTypeException.

7. Práca s poľom

MethodHandles nie sú určené na prácu iba s poľami alebo objektmi, ale aj s poľami. V skutočnosti s asSpreader () API, je možné vytvoriť handle metódy šírenia poľa.

V tomto prípade rukoväť metódy prijíma argument poľa, pričom jeho prvky šíri ako pozičné argumenty a voliteľne dĺžku poľa.

Pozrime sa, ako môžeme rozložiť popisovač metódy a skontrolovať, či sú prvky v poli rovnaké:

MethodType mt = MethodType.methodType (boolean.class, Object.class); MethodHandle equals = publicLookup.findVirtual (String.class, "equals", mt); MethodHandle methodHandle = equals.asSpreader (Object []. Trieda, 2); assertTrue ((boolean) methodHandle.invoke (nový objekt [] {"java", "java"})));

8. Vylepšenie metódy

Keď sme definovali popisovač metódy, je možné ju vylepšiť tak, že sa popisovač metódy zviaže na argument bez toho, aby sme ho skutočne vyvolali.

Napríklad v prostredí Java 9 sa tento druh správania používa na optimalizáciu String zreťazenie.

Pozrime sa, ako môžeme vykonať zreťazenie, ktoré zviaže príponu k nášmu concatMH:

MethodType mt = MethodType.methodType (String.class, String.class); MethodHandle concatMH = publicLookup.findVirtual (String.class, "concat", mt); MethodHandle bindedConcatMH = concatMH.bindTo ("Dobrý deň"); assertEquals ("Hello World!", bindedConcatMH.invoke ("svet!"));

9. Vylepšenia Java 9

V prostredí Java 9 bolo v aplikácii urobených niekoľko vylepšení MethodHandles API s cieľom uľahčiť jeho používanie.

Vylepšenia sa týkali 3 hlavných tém:

  • Vyhľadávacie funkcie - umožnenie vyhľadávania tried z rôznych kontextov a podpora neabstrahových metód v rozhraniach
  • Riešenie argumentov - zdokonalenie funkcií skladania, zhromažďovania a šírenia argumentov
  • Ďalšie kombinácie - pridanie slučiek (slučka, whileLoop, doWhileLoop ...) a lepšiu podporu pri riešení výnimiek s skústeFinally

Tieto zmeny priniesli niekoľko ďalších výhod:

  • Zvýšená optimalizácia kompilátora JVM
  • Zníženie inštancie
  • Povolená presnosť pri používaní súboru MethodHandles API

Podrobnosti o vykonaných vylepšeniach sú k dispozícii na stránke MethodHandles API Javadoc.

10. Záver

V tomto článku sme sa zaoberali MethodHandles API, čo to je a ako ich môžeme použiť.

Diskutovali sme tiež o tom, ako to súvisí s rozhraním Reflection API, a keďže popisovače metódy umožňujú operácie na nízkej úrovni, malo by byť lepšie vyhnúť sa ich použitiu, pokiaľ nezodpovedajú dokonale rozsahu úlohy.

Úplný zdrojový kód tohto článku je ako vždy k dispozícii na stránkach Github.


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