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.