Metodehåndtak i Java

1. Introduksjon

I denne artikkelen skal vi utforske et viktig API som ble introdusert i Java 7 og forbedret i følgende versjoner, java.lang.invoke.MethodHandles.

Spesielt lærer vi hvilken metodehåndtak er, hvordan du lager dem og hvordan du bruker dem.

2. Hva er metodehåndtak?

Kommer til definisjonen, som angitt i API-dokumentasjonen:

Et metodeshåndtak er en skrevet, direkte kjørbar referanse til en underliggende metode, konstruktør, felt eller lignende operasjon på lavt nivå, med valgfrie transformasjoner av argumenter eller returverdier.

På en enklere måte, metodehåndtak er en lavnivåmekanisme for å finne, tilpasse og påkalle metoder.

Metodehåndtak er uforanderlige og har ingen synlig tilstand.

For å lage og bruke en Metode Håndtak, 4 trinn kreves:

  • Opprette oppslag
  • Opprette metodetypen
  • Finne metodehåndtaket
  • Påkaller metodehåndtaket

2.1. Metodehåndtak vs refleksjon

Metodehåndtak ble introdusert for å fungere ved siden av det eksisterende java.lang.refleksjon API, siden de tjener forskjellige formål og har forskjellige egenskaper.

Fra et ytelsesperspektiv er den Metode Håndtak API kan være mye raskere enn Reflection API siden tilgangskontrollene utføres ved opprettelsestidspunktet i stedet for ved utførelsestidspunktet. Denne forskjellen blir forsterket hvis en sikkerhetsleder er til stede, siden medlems- og klassesøk er underlagt ytterligere kontroller.

Men med tanke på at ytelse ikke er det eneste egnethetsmål for en oppgave, må vi også vurdere at Metode Håndtak API er vanskeligere å bruke på grunn av mangel på mekanismer som medlemsklassetelling, tilgjengelighetsflagginspeksjon og mer.

Likevel, den Metode Håndtak API gir muligheten til å curry metoder, endre parametertypene og endre rekkefølgen.

Å ha en klar definisjon og mål for Metode Håndtak API, kan vi nå begynne å jobbe med dem, fra oppslaget.

3. Opprette Se opp

Det første vi skal gjøre når vi vil lage et metodeshåndtak er å hente oppslaget, fabrikkobjektet som er ansvarlig for å lage metodehåndtak for metoder, konstruktører og felt, som er synlige for oppslagsklassen.

Gjennom Metode Håndtak API, det er mulig å opprette oppslagsobjektet, med forskjellige tilgangsmoduser.

La oss lage oppslaget som gir tilgang til offentlig metoder:

MethodHandles.Lookup publicLookup = MethodHandles.publicLookup ();

Imidlertid, hvis vi vil ha tilgang også til privat og beskyttet metoder, kan vi i stedet bruke se opp() metode:

MethodHandles.Lookup lookup = MethodHandles.lookup ();

4. Opprette en MethodType

For å kunne lage Metode Håndtak, krever oppslagsobjektet en definisjon av sin type, og dette oppnås gjennom MethodType klasse.

Spesielt, en MethodType representerer argumentene og returtypen akseptert og returnert av et metodeshåndtak eller bestått og forventet av en metodehåndteringsanrop.

Strukturen til en MethodType er enkel og den er dannet av en returtype sammen med et passende antall parametertyper som må matches riktig mellom et metodeshåndtak og alle dets innringere.

På samme måte som Metode Håndtak, til og med forekomster av en MethodType er uforanderlige.

La oss se hvordan det er mulig å definere en MethodType som spesifiserer en java.util.Liste klasse som returtype og en Gjenstand array som inngangstype:

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

I tilfelle metoden returnerer en primitiv type eller tomrom som returtype, vil vi bruke klassen som representerer disse typene (void.class, int.class ...).

La oss definere en MethodType som returnerer en int-verdi og aksepterer en Gjenstand:

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

Vi kan nå fortsette å lage Metode Håndtak.

5. Finne en Metode Håndtak

Når vi har definert metodetypen vår, for å lage en Metode Håndtak, vi må finne det gjennom se opp eller publicLookup objekt, og oppgir også opprinnelseklassen og metodenavnet.

Spesielt gir oppslagsfabrikken et sett med metoder som lar oss finne metodehåndtaket på en passende måte med tanke på omfanget av metoden vår. Starter med det enkleste scenariet, la oss utforske de viktigste.

5.1. Metodehåndtak for metoder

Bruker findVirtual () metode tillater oss å lage en MethodHandle for en objektmetode. La oss lage en, basert på concat () metoden for String klasse:

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

5.2. Metodehåndtak for statiske metoder

Når vi ønsker å få tilgang til en statisk metode, kan vi i stedet bruke findStatic () metode:

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

I dette tilfellet opprettet vi et metodeshåndtak som konverterer en rekke Objekter til en Liste av dem.

5.3. Metodehåndtak for konstruktører

Å få tilgang til en konstruktør kan gjøres ved hjelp av findConstructor () metode.

La oss lage en metodehåndtak som oppfører seg som konstruktøren av Heltall klasse, akseptere en String Egenskap:

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

5.4. Metodehåndtak for felt

Ved hjelp av et metodeshåndtak er det mulig å få tilgang også til felt.

La oss begynne å definere Bok klasse:

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

Når vi har som forutsetning en direkte tilgangssynlighet mellom metodehåndtaket og den deklarerte eiendommen, kan vi lage et metodeshåndtak som oppfører seg som en getter:

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

For mer informasjon om håndtering av variabler / felt, se på Java 9 Variable Handles Demystified, der vi diskuterer java.lang.invoke.VarHandle API, lagt til i Java 9.

5.5. Metodehåndtak for private metoder

Å lage et metodeshåndtak for en privat metode kan gjøres, ved hjelp av java.lang.refleksjon API.

La oss begynne å legge til en privat metoden til Bok klasse:

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

Nå kan vi lage et metodeshåndtak som oppfører seg nøyaktig som formatBook () metode:

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

6. Påkalle et metodehåndtak

Når vi har laget metodehåndtakene våre, er det neste trinn å bruke dem. Spesielt den Metode Håndtak klasse gir 3 forskjellige måter å utføre et metodeshåndtak på: påkalle (), påkalleWithArugments () og påkalle Eksakt ().

La oss starte med påkalle alternativ.

6.1. Påkalle et metodehåndtak

Når du bruker påkalle () metoden, håndhever vi antall argumenter (arity) som skal løses, men vi tillater utførelse av casting og boksing / unboxing av argumentene og returtypene.

La oss se hvordan det er mulig å bruke påkalle () med et innrammet argument:

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

I dette tilfellet erstattMH krever røye argumenter, men påkalle () utfører en unboxing på Karakter argument før utførelsen.

6.2. Påkaller med argumenter

Påkalle et metodeshåndtak ved hjelp av påkalleWithArguments metoden, er den minst begrensende av de tre alternativene.

Faktisk tillater det en variabel arititetsanrop, i tillegg til casting og boksing / unboxing av argumentene og av returtypene.

Kommer til praksis, dette lar oss lage en Liste av Heltall starter fra en array av int verdier:

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åkaller Exact

I tilfelle vi ønsker å være mer restriktive i måten vi utfører et metodeshåndtak på (antall argumenter og deres type), må vi bruke påkalle Eksakt () metode.

Faktisk gir det ikke noen rollebesetning til klassen som tilbys, og krever et fast antall argumenter.

La oss se hvordan vi kan sum to int verdier ved hjelp av et metodeshåndtak:

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

Hvis vi i dette tilfellet bestemmer oss for å overføre til påkalle Eksakt metode et tall som ikke er et int, vil påkallelsen føre til WrongMethodTypeException.

7. Arbeide med Array

Metode Håndtak er ikke ment å fungere bare med felt eller gjenstander, men også med matriser. Faktisk, med asSpreder () API, det er mulig å lage et array-spreading method-håndtak.

I dette tilfellet aksepterer metodehåndtaket et arrayargument som sprer elementene som posisjonsargumenter og eventuelt lengden på arrayet.

La oss se hvordan vi kan spre et metodeshåndtak for å sjekke om elementene i en matrise er like:

MethodType mt = MethodType.methodType (boolsk.klasse, Objekt.klasse); MethodHandle er lik = publicLookup.findVirtual (String.class, "equals", mt); MethodHandle methodHandle = er lik.asSpreder (Objekt []. Klasse, 2); assertTrue ((boolean) methodHandle.invoke (nytt objekt [] {"java", "java"}));

8. Forbedre et metodeshåndtak

Når vi har definert et metodeshåndtak, er det mulig å forbedre det ved å binde metodehåndtaket til et argument uten å påkalle det.

For eksempel, i Java 9, brukes denne typen oppførsel for å optimalisere String sammenkobling.

La oss se hvordan vi kan utføre en sammenkobling og binde et suffiks til vår concatMH:

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

9. Java 9 Forbedringer

Med Java 9 ble det gjort få forbedringer av Metode Håndtak API med sikte på å gjøre det mye enklere å bruke.

Forbedringene berørte tre hovedtemaer:

  • Oppslagsfunksjoner - tillate klassesøk fra forskjellige sammenhenger og støtte ikke-abstrakte metoder i grensesnitt
  • Argumenthåndtering - forbedring av argumentfolding, argumentinnsamling og argumentspredningsfunksjonalitet
  • Ekstra kombinasjoner - legge til løkker (Løkke, whileLoop, doWhileLoop ...) og et bedre unntak håndteringsstøtte med prøv til slutt

Disse endringene resulterte i få ekstra fordeler:

  • Økte JVM-kompilatoroptimaliseringer
  • Instantiering reduksjon
  • Aktivert presisjon i bruken av Metode Håndtak API

Detaljer om forbedringene som er gjort, er tilgjengelige på Metode Håndtak API Javadoc.

10. Konklusjon

I denne artikkelen dekket vi Metode Håndtak API, hva de er og hvordan vi kan bruke dem.

Vi diskuterte også hvordan det er relatert til Reflection API, og siden metodehåndtakene tillater operasjoner på lavt nivå, bør det være bedre å unngå å bruke dem, med mindre de passer perfekt til jobben.

Som alltid er den fullstendige kildekoden for denne artikkelen tilgjengelig på Github.


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