A Method kezel Java-ban

1. Bemutatkozás

Ebben a cikkben egy fontos API-t fogunk felfedezni, amelyet a Java 7-ben vezettek be és tovább fejlesztettek a következő verziókban, a java.lang.invoke.MethodHandles.

Különösen megtudjuk, hogy milyen módszerek kezelik a módszereket, hogyan kell létrehozni és hogyan kell használni őket.

2. Mik a módszer fogantyúi?

Az API dokumentációjában szereplő definícióhoz jutva:

A metódus fogantyú egy tipikus, közvetlenül végrehajtható hivatkozás egy mögöttes módszerre, konstruktorra, mezőre vagy hasonló alacsony szintű műveletre, opcionálisan átalakítva az argumentumokat vagy a visszatérő értékeket.

Egyszerűbb módon, A metódusfogantyúk alacsony szintű mechanizmusok a módszerek megtalálásához, adaptálásához és meghívásához.

A módszer fogantyúi változhatatlanok és nincs látható állapotuk.

Az a létrehozásához és használatához MethodHandle, 4 lépés szükséges:

  • A keresés létrehozása
  • A módszer típusának létrehozása
  • A módszer fogantyújának megkeresése
  • A metódus fogantyújának meghívása

2.1. A módszer kezeli a reflexiót

Módszer fogantyúkat vezettek be annak érdekében, hogy a meglévők mellett működjenek java.lang.reflect API, mivel különböző célokat szolgálnak és eltérő jellemzőkkel rendelkeznek.

Teljesítmény szempontjából a MethodHandles Az API sokkal gyorsabb lehet, mint a Reflection API, mivel a hozzáférés-ellenőrzéseket a létrehozás, nem pedig a végrehajtás idején hajtják végre. Ez a különbség felerősödik, ha egy biztonsági menedzser van jelen, mivel a tagok és az osztályok keresését további ellenőrzéseknek vetik alá.

Tekintettel azonban arra, hogy a teljesítmény nem az egyetlen alkalmasságmérő egy feladatra, azt is figyelembe kell vennünk, hogy a MethodHandles Az API-t nehezebb használni olyan mechanizmusok hiánya miatt, mint a tagosztályok felsorolása, az akadálymentességi jelzők ellenőrzése és még sok más.

Ennek ellenére a MethodHandles Az API lehetőséget kínál a módszerek curry-re, a paraméterek típusának és sorrendjének megváltoztatására.

Világos meghatározása és céljai MethodHandles API, most már elkezdhetünk dolgozni velük, a kereséstől kezdve.

3. A Nézz fel

Az első tennivaló, amikor metóduskezelőt akarunk létrehozni, az a lekérés, a gyári objektum, amely felelős a metódusok, konstruktorok és mezők metódusfogantyúinak létrehozásáért, amelyek a keresési osztály számára láthatók.

Keresztül MethodHandles API, lehetséges a keresési objektum létrehozása különböző hozzáférési módokkal.

Hozzuk létre a hozzáférést biztosító keresést nyilvános mód:

MethodHandles.Lookup publicLookup = MethodHandles.publicLookup ();

Abban az esetben azonban, ha szeretnénk hozzáférést is kapni magán és védett módszerek helyett használhatjuk a Nézz fel() módszer:

MethodHandles.Lookup lookup = MethodHandles.lookup ();

4. A. Létrehozása MethodType

Annak érdekében, hogy létre lehessen hozni a MethodHandle, a keresési objektumnak meg kell határoznia a típusát, és ezt a MethodType osztály.

Különösen, a MethodType a argumentumokat és a return típust jelöli, amelyet egy metóduskezelő fogadott el és adott vissza, vagy amelyet a metóduskezelő hívója átadott és elvárt.

Az a felépítése MethodType egyszerű, és egy visszatérési típus, valamint megfelelő számú paramétertípus alkotja, amelyeknek megfelelően össze kell egyezniük a metódusfogantyú és az összes hívója között.

Ugyanúgy, mint MethodHandle, még az a MethodType változhatatlanok.

Nézzük meg, hogyan lehet meghatározni a MethodType amely meghatározza a java.util.List osztály mint visszatérési típus és egy Tárgy tömb mint bemeneti típus:

MethodType mt = MethodType.methodType (Lista.osztály, Objektum []. Osztály);

Abban az esetben, ha a módszer egy primitív típust ad vissza, vagy üres visszatérési típusaként az adott típusokat képviselő osztályt fogjuk használni (void.class, int.class ...).

Határozzuk meg a MethodType amely visszaad egy int értéket és elfogad egy Tárgy:

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

Most folytathatjuk az alkotást MethodHandle.

5. Megtalálása a MethodHandle

Miután meghatároztuk a módszer típusát, a MethodHandle, meg kell találnunk a Nézz fel vagy publicLookup objektum, megadva az origó osztályt és a metódus nevét is.

Különösen a kereső gyár kínál olyan módszerek sorozatát, amelyek lehetővé teszik számunkra, hogy a módszer alkalmazási körét figyelembe véve megfelelő módon megtaláljuk a módszer fogantyút. A legegyszerűbb forgatókönyvtől kezdve vizsgáljuk meg a főbbeket.

5.1. Módszer kezelése a módszerekhez

Használni a findVirtual () metódus lehetővé teszi, hogy hozzunk létre egy MethodHandle-t egy objektum-metódushoz. Hozzunk létre egyet a concat () módszere Húr osztály:

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

5.2. Módszer kezelése statikus módszerekhez

Amikor hozzáférést akarunk szerezni egy statikus módszerhez, akkor ehelyett használhatjuk a findStatic () módszer:

MethodType mt = MethodType.methodType (Lista.osztály, Objektum []. Osztály); MethodHandle asListMH = publicLookup.findStatic (Tömbök.osztály, "asList", mt);

Ebben az esetben létrehoztunk egy metódust, amely átalakítja a tömböt Tárgyak a Lista tőlük.

5.3. Módszer fogantyú a kivitelezők számára

A konstruktorhoz való hozzáférés a findConstructor () módszer.

Hozzunk létre egy metódust, amely a Egész szám osztály, elfogadva a Húr tulajdonság:

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

5.4. Metóduskezelő mezőkhöz

A metódusfogantyúval a mezőkhöz is hozzáférhet.

Kezdjük meghatározni a Könyv osztály:

nyilvános osztály Könyv {String id; Karakterlánc címe; // konstruktor}

Előfeltételként, hogy a metódus fogantyúja és a deklarált tulajdonság között közvetlen hozzáférési láthatóság legyen, létrehozhatunk egy metszet fogantyút, amely getterként viselkedik:

MethodHandle getTitleMH = lookup.findGetter (Könyv.osztály, "cím", Karakterlánc.osztály);

A változók / mezők kezelésével kapcsolatos további információkért tekintse meg a Java 9 Varyst Handles Demystified címet, ahol a Java 9-ben hozzáadott java.lang.invoke.VarHandle API-t tárgyaljuk.

5.5. Módszer kezelése magán módszerekhez

Létrehozhatunk egy metóduskezelőt egy privát módszerhez, a java.lang.reflect API.

Kezdjük hozzá a magán módszer a Könyv osztály:

privát karakterlánc-formátumkönyv () {return id + ">" + title; }

Most létrehozhatunk egy metódust, amely pontosan úgy viselkedik, mint a formatBook () módszer:

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

6. A Method Handle meghívása

Miután elkészítettük a módszeres fogantyúkat, a következő lépés azok használata. Különösen a MethodHandle osztály 3 különböző módszert kínál a metóduskezelés végrehajtására: invoke (), invokeWithArugments () és invokeExact ().

Kezdjük a hivatkozni választási lehetőség.

6.1. A Method Handle meghívása

A invoke () módszerrel kikényszerítjük a rögzítendő argumentumok (aritás) számát, de megengedjük az argumentumok és a visszatérési típusok leadását, boxolását / kibontását.

Nézzük meg, hogyan lehetséges a invoke () dobozos érvvel:

MethodType mt = MethodType.methodType (karakterlánc, osztály, osztály, osztály); MethodHandle helyettesítésMH = publicLookup.findVirtual (String.osztály, "csere", mt); Karakterlánc kimenet = (Karakterlánc) helyettesíti a MH.invoke ("jovo", Character.valueOf ('o'), 'a'); assertEquals ("java", output);

Ebben az esetben a cserélje ki MH igényel char érvek, de a invoke () kicsomagolást végez a karakter érv a végrehajtása előtt.

6.2. Hívás érvekkel

Metódkezelő meghívása a invokeWithArguments módszer, a legkevésbé korlátozó a három lehetőség közül.

Valójában ez lehetővé teszi az aritás változó meghívását, az argumentumok és a visszatérési típusok leadása, boxolása / kibontása mellett.

Gyakorlatra érkezve ez lehetővé teszi számunkra a Lista nak,-nek Egész szám kezdve egy sor nak,-nek int értékek:

MethodType mt = MethodType.methodType (Lista.osztály, Objektum []. Osztály); MethodHandle asList = publicLookup.findStatic (Tömbök.osztály, "asList", mt); Lista lista = (Lista) asList.invokeWithArguments (1,2); assertThat (Tömbök.asList (1,2), is (lista));

6.3. Pontos hívása

Abban az esetben, ha korlátozni akarjuk a metóduskezelés végrehajtását (argumentumok száma és típusa), akkor a invokeExact () módszer.

Valójában nem nyújt castingot a megadott osztályhoz, és rögzített számú argumentumot igényel.

Lássuk, hogyan tudunk összeg kettő int értékek egy metódus fogantyúval:

MethodType mt = MethodType.methodType (int.osztály, int.osztály, int.osztály); MethodHandle összegMH = lookup.findStatic (Integer.class, "sum", mt); int összeg = (int) sumMH.invokeExact (1, 11); assertEquals (12, összeg);

Ha ebben az esetben úgy döntünk, hogy átmegyünk a invokeExact metódus egy olyan szám, amely nem egy int, az invokáció oda vezet WrongMethodTypeException.

7. Munka az Array-vel

MethodHandles nem csak mezőkkel vagy objektumokkal, hanem tömbökkel is használhatók. Ami azt illeti, a asSpreader () API, lehetséges egy tömbterjesztéses módszer kezelése.

Ebben az esetben a metóduskezelő elfogad egy tömb argumentumot, elemeit terjesztve pozicionális argumentumként, és adott esetben a tömb hosszát.

Nézzük meg, hogyan terjeszthetjük a metódus fogantyúját annak ellenőrzésére, hogy a tömb elemei megegyeznek-e:

MethodType mt = MethodType.methodType (logikai.osztály, Object.osztály); MethodHandle egyenlő = publicLookup.findVirtual (String.osztály, "egyenlő", mt); MethodHandle methodHandle = egyenlő.asSpreader (Object []. Osztály, 2); assertTrue ((logikai) methodHandle.invoke (új objektum [] {"java", "java"}));

8. A módszer kezelése

Miután meghatároztuk a metódus fogantyúját, tovább lehet javítani úgy, hogy a metódus fogantyút egy argumentumhoz kötjük anélkül, hogy valóban meghívnánk.

Például a Java 9-ben ezt a fajta viselkedést használják az optimalizálásra Húr összefűzés.

Lássuk, hogyan tudunk összefűzni, utótagot kötni a sajátunkhoz concatMH:

MethodType mt = MethodType.methodType (String.osztály, String.osztály); MethodHandle concatMH = publicLookup.findVirtual (String.osztály, "concat", mt); MethodHandle bindedConcatMH = concatMH.bindTo ("Hello"); assertEquals ("Hello World!", bindedConcatMH.invoke ("World!"));

9. Java 9 fejlesztések

A Java 9 alkalmazással kevés fejlesztés történt a MethodHandles API azzal a céllal, hogy sokkal könnyebb legyen használni.

A fejlesztések 3 fő témát érintettek:

  • Keresési funkciók - lehetővé teszi az osztálykereséseket különböző kontextusokból, és támogatja az elvont módszereket az interfészekben
  • Érvkezelés - az argumentumhajtás, az argumentumgyűjtés és az argumentumterjesztés funkcióinak fejlesztése
  • További kombinációk - hurkok hozzáadása (hurok, whileLoop, doWhileLoop…) és egy jobb kivétel kezelése támogatja a próbáld meg végül

Ezek a változások néhány további előnnyel jártak:

  • Fokozott JVM fordító optimalizálás
  • Azonnali csökkentés
  • Engedélyezte a pontosságot a MethodHandles API

Az elvégzett fejlesztések részletei a MethodHandles API Javadoc.

10. Következtetés

Ebben a cikkben kitértünk a MethodHandles API, mik ezek és hogyan tudjuk használni őket.

Megbeszéltük azt is, hogy ez hogyan kapcsolódik a Reflection API-hoz, és mivel a módszer kezelõi alacsony szintû mûveleteket tesznek lehetõvé, jobb lenne kerülni azok használatát, hacsak nem felelnek meg tökéletesen a feladat körének.

Mint mindig, a cikk teljes forráskódja elérhető a Github oldalon.