Metodehåndtag i Java

1. Introduktion

I denne artikel vil vi undersøge en vigtig API, der blev introduceret i Java 7 og forbedret i de følgende versioner, den java.lang.invoke.MethodHandles.

Vi lærer især, hvilken metode håndtag er, hvordan man opretter dem, og hvordan man bruger dem.

2. Hvad er metodehåndtag?

Kommer til sin definition som anført i API-dokumentationen:

Et metodehåndtag er en indtastet, direkte eksekverbar henvisning til en underliggende metode, konstruktør, felt eller lignende operation på lavt niveau med valgfri transformationer af argumenter eller returværdier.

På en enklere måde metodehåndtag er en mekanisme på lavt niveau til at finde, tilpasse og påkalde metoder.

Metodehåndtag er uforanderlige og har ingen synlig tilstand.

Til oprettelse og brug af en Metode Håndtag, 4 trin kræves:

  • Oprettelse af opslag
  • Oprettelse af metodetypen
  • Find metodehåndtaget
  • Påkald af metodens håndtag

2.1. Metodehåndtag vs refleksion

Metodehåndtag blev introduceret for at arbejde sammen med det eksisterende java.lang.reflekteret API, da de tjener forskellige formål og har forskellige egenskaber.

Fra et præstationssynspunkt er den Metode Håndtag API kan være meget hurtigere end Reflection API, da adgangskontrollen foretages på oprettelsestidspunktet snarere end på udførelsestidspunktet. Denne forskel forstærkes, hvis en sikkerhedschef er til stede, da medlems- og klassesøgninger er underlagt yderligere kontrol.

I betragtning af at ydeevne ikke er den eneste egnethedsmåling til en opgave, skal vi dog også overveje, at Metode Håndtag API er sværere at bruge på grund af manglen på mekanismer som optælling af medlemsklasser, inspektion af tilgængelighedsflag og mere.

Alligevel er det Metode Håndtag API giver mulighed for at karrymetoder, ændre parametertyperne og ændre deres rækkefølge.

At have en klar definition og mål for Metode Håndtag API, vi kan nu begynde at arbejde med dem startende fra opslaget.

3. Oprettelse af Kig op

Den første ting, der skal gøres, når vi vil oprette et metodeshåndtag, er at hente opslaget, det fabriksobjekt, der er ansvarligt for at oprette metodehåndtag til metoder, konstruktører og felter, der er synlige for opslagsklassen.

Gennem Metode Håndtag API, det er muligt at oprette opslagsobjektet med forskellige adgangstilstande.

Lad os oprette det opslag, der giver adgang til offentlig metoder:

MethodHandles.Lookup publicLookup = MethodHandles.publicLookup ();

Men hvis vi også vil have adgang til privat og beskyttet metoder, kan vi i stedet bruge kig op() metode:

MethodHandles.Lookup lookup = MethodHandles.lookup ();

4. Oprettelse af en MethodType

For at være i stand til at oprette Metode Håndtag, kræver opslagsobjektet en definition af sin type, og dette opnås gennem MethodType klasse.

I særdeleshed, -en MethodType repræsenterer argumenterne og returtypen accepteret og returneret af et metodeshåndtag eller bestået og forventet af en metodehåndtereropkald.

Opbygningen af ​​en MethodType er enkel og den er dannet af en returtype sammen med et passende antal parametertyper, der skal matches korrekt mellem et metodeshåndtag og alle dets opkaldere.

På samme måde som Metode Håndtag, selv forekomsterne af en MethodType er uforanderlige.

Lad os se, hvordan det er muligt at definere en MethodType der angiver en java.util.List klasse som returtype og en Objekt array som input type:

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

Hvis metoden returnerer en primitiv type eller ugyldig som sin returtype, bruger vi klassen, der repræsenterer disse typer (void.class, int.class ...).

Lad os definere en MethodType der returnerer en int-værdi og accepterer en Objekt:

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

Vi kan nu fortsætte med at skabe Metode Håndtag.

5. Finde en Metode Håndtag

Når vi har defineret vores metodetype, for at oprette en Metode Håndtag, vi er nødt til at finde det gennem kig op eller publicLookup objekt, der også angiver oprindelseklassen og metodens navn.

Især giver opslagsfabrikken et sæt metoder, der giver os mulighed for at finde metodens håndtag på en passende måde i betragtning af omfanget af vores metode. Start med det enkleste scenario, lad os udforske de vigtigste.

5.1. Metodehåndtag til metoder

Bruger findVirtual () metode tillader os at oprette en MethodHandle til en objektmetode. Lad os oprette en, baseret på concat () metode til Snor klasse:

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

5.2. Metodehåndtag til statiske metoder

Når vi ønsker at få adgang til en statisk metode, kan vi i stedet bruge findStatic () metode:

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

I dette tilfælde oprettede vi et metodeshåndtag, der konverterer en matrix af Objekter til en Liste af dem.

5.3. Metodehåndtag til konstruktører

At få adgang til en konstruktør kan gøres ved hjælp af findConstructor () metode.

Lad os oprette en metodehåndtag, der opfører sig som konstruktøren af Heltal klasse, accepterer en Snor attribut:

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

5.4. Metodehåndtag til felter

Ved hjælp af et metodeshåndtag er det muligt at få adgang også til felter.

Lad os begynde at definere Bestil klasse:

public class Book {String id; String titel; // konstruktør}

Når vi som forudsætning har en direkte adgangssynlighed mellem metodehåndtaget og den deklarerede ejendom, kan vi oprette et metodeshåndtag, der opfører sig som en getter:

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

For yderligere oplysninger om håndtering af variabler / felter, se på Java 9 Variable Handles Demystified, hvor vi diskuterer java.lang.invoke.VarHandle API, tilføjet i Java 9.

5.5. Metodehåndtering til private metoder

Oprettelse af et metodeshåndtag til en privat metode kan gøres ved hjælp af java.lang.reflekteret API.

Lad os begynde at tilføje en privat metode til Bestil klasse:

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

Nu kan vi oprette et metodehåndtag, der opfører sig nøjagtigt som formatBook () metode:

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

6. Påkald af et metodehåndtag

Når vi har oprettet vores metodehåndtag, er det næste trin at bruge dem. Især den Metode Håndtag klasse giver 3 forskellige måder at udføre et metodeshåndtag på: påberåbe sig (), påkaldeWithArugments () og påkalde Eksakt ().

Lad os starte med påberåbe sig mulighed.

6.1. Påkald af et metodehåndtag

Når du bruger påberåbe sig () metode håndhæver vi antallet af argumenter (arity), der skal rettes, men vi tillader udførelse af casting og boksning / unboxing af argumenterne og returneringstyperne.

Lad os se, hvordan det er muligt at bruge påberåbe sig () med et indrammet argument:

MethodType mt = MethodType.methodType (String.class, char.class, char.class); MethodHandle erstatteMH = publicLookup.findVirtual (String.class, "udskift", mt); String output = (String) erstattMH.invoke ("jovo", Character.valueOf ('o'), 'a'); assertEquals ("java", output);

I dette tilfælde er udskiftMH kræver char argumenter, men påberåbe sig () udfører en unboxing på Karakter argument før dens udførelse.

6.2. Påberåber sig med argumenter

Påkald af et metodeshåndtag ved hjælp af påkaldeWithArguments metode, er den mindst begrænsende af de tre muligheder.

Faktisk tillader det en variabel aritetsopkald ud over casting og boksning / unboxing af argumenterne og af returtyperne.

Når vi kommer til praksis, giver dette os mulighed for at oprette en Liste af Heltal startende fra en array af int værdier:

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

6.3. Påkalder nøjagtig

Hvis vi ønsker at være mere restriktive i den måde, vi udfører et metodeshåndtag på (antal argumenter og deres type), skal vi bruge påkaldExact () metode.

Faktisk giver det ikke nogen casting til den angivne klasse og kræver et fast antal argumenter.

Lad os se, hvordan vi kan sum to int værdier ved hjælp af et metodeshåndtag:

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, sum);

Hvis vi i dette tilfælde beslutter at videregive til påkalde Eksakt metode et tal, der ikke er et int, vil påkaldelsen føre til WrongMethodTypeException.

7. Arbejde med Array

Metode Håndtag er ikke beregnet til kun at arbejde med felter eller objekter, men også med arrays. Som en kendsgerning, med som spreder () API, det er muligt at lave et array-spreading method-håndtag.

I dette tilfælde accepterer metodehåndtaget et arrayargument, der spreder dets elementer som positionelle argumenter og eventuelt længden af ​​arrayet.

Lad os se, hvordan vi kan sprede et metodeshåndtag for at kontrollere, om elementerne i en matrix er lig med:

MethodType mt = MethodType.methodType (boolsk.klasse, Objekt.klasse); MethodHandle er lig = publicLookup.findVirtual (String.class, "lig", mt); MethodHandle methodHandle = er lig med.asSpreader (objekt []. Klasse, 2); assertTrue ((boolean) methodHandle.invoke (nyt objekt [] {"java", "java"}));

8. Forbedring af et metodeshåndtag

Når vi først har defineret et metodeshåndtag, er det muligt at forbedre det ved at binde metodens håndtag til et argument uden faktisk at påberåbe det.

For eksempel i Java 9 bruges denne form for adfærd til at optimere Snor sammenkædning.

Lad os se, hvordan vi kan udføre en sammenkædning, der binder et suffiks til vores concatMH:

MethodType mt = MethodType.methodType (String.class, String.class); MethodHandle concatMH = publicLookup.findVirtual (String.class, "concat", mt); MethodHandle bundetConcatMH = concatMH.bindTo ("Hej"); assertEquals ("Hello World!", bindedConcatMH.invoke ("World!"));

9. Java 9 forbedringer

Med Java 9 blev der foretaget få forbedringer af Metode Håndtag API med det formål at gøre det meget nemmere at bruge.

Forbedringerne berørte 3 hovedemner:

  • Opslagsfunktioner - tillader klassesøgninger fra forskellige sammenhænge og understøtter ikke-abstrakte metoder i grænseflader
  • Argumenthåndtering - forbedring af foldning af argumenter, indsamling af argumenter og spredning af argumenter
  • Yderligere kombinationer - tilføjelse af sløjfer (løkke, whileLoop, doWhileLoop ...) og en bedre undtagelse håndteringsstøtte med prøv Endelig

Disse ændringer resulterede i få yderligere fordele:

  • Øgede JVM-kompilatoroptimeringer
  • Øjeblikkelig reduktion
  • Aktiveret præcision i brugen af Metode Håndtag API

Detaljer om forbedringerne er tilgængelige på Metode Håndtag API Javadoc.

10. Konklusion

I denne artikel dækkede vi Metode Håndtag API, hvad de er, og hvordan vi kan bruge dem.

Vi diskuterede også, hvordan det er relateret til Reflection API, og da metodehåndtagene tillader operationer på lavt niveau, bør det være bedre at undgå at bruge dem, medmindre de passer perfekt til opgavens omfang.

Som altid er den komplette kildekode til denne artikel tilgængelig på Github.


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