Programmering

Kom i gang med metodereferanser i Java

Sammen med lambdas brakte Java SE 8 metodereferanser til Java-språket. Denne opplæringen gir en kort oversikt over metodereferanser i Java, så begynner du å bruke dem med eksempler på Java-kode. Ved slutten av opplæringen vil du vite hvordan du bruker metodereferanser for å referere til klassens statiske metoder, bundet og ubundet ikke-statiske metoder, og konstruktører, samt hvordan du bruker dem til å referere til instansmetoder i superklasse og nåværende klasse typer. Du vil også forstå hvorfor mange Java-utviklere har tatt i bruk lambdauttrykk og metodereferanser som et renere, enklere alternativ til anonyme klasser.

Merk at kodeeksempler i denne opplæringen er kompatible med JDK 12.

last ned Få koden Last ned kildekoden for eksempel applikasjoner i denne opplæringen. Skapt av Jeff Friesen for JavaWorld.

Metodehenvisninger: En grunning

Min forrige Java 101-opplæring introduserte lambda-uttrykk, som brukes til å definere anonyme metoder som deretter kan behandles som forekomster av et funksjonelt grensesnitt. Noen ganger gjør et lambdauttrykk ingenting mer enn å kalle en eksisterende metode. For eksempel bruker følgende kodefragment en lambda til å påkalle System.outs ugyldig utskrift (er) metoden på lambdas eneste argument -stypen er ennå ikke kjent:

(s) -> System.out.println (s)

Lambda presenterer (s) som sin formelle parameterliste og en kodetype hvis System.out.println (s) uttrykk utskrifter sverdi til standard utgangsstrøm. Den har ikke en eksplisitt grensesnitttype. I stedet utleder kompilatoren fra den omgivende sammenhengen hvilket funksjonelt grensesnitt som skal instantieres. Tenk for eksempel på følgende kodefragment:

Forbrukerforbruker = (s) -> System.out.println (s);

Kompilatoren analyserer forrige erklæring og fastslår at java.util.function.Consumer forhåndsdefinerte funksjonelle grensesnitt ugyldig godta (T t) metoden samsvarer med lambdas formelle parameterliste ((s)). Det bestemmer også det aksepterer()s tomrom retur type treff println ()s tomrom returtype. Lambda er altså bundet til Forbruker.

Mer spesifikt er lambda bundet til Forbruker. Kompilatoren genererer kode slik at en påkalling av Forbrukers ugyldig godta (streng s) metoden resulterer i strengargumentet sendt til s blir overført til System.outs ugyldig utskrift (streng s) metode. Denne påkallelsen er vist nedenfor:

consumer.accept ("Hei"); // Gi "Hallo" til lambdakroppen. Skriv ut Hello til standard utdata.

For å lagre tastetrykk kan du erstatte lambda med en metodehenvisning, som er en kompakt referanse til en eksisterende metode. For eksempel erstatter følgende kodefragment (Streng s) -> System.out.println (s) med System.out :: println, hvor :: betyr det System.outs ugyldig utskrift (streng s) metoden refereres til:

Forbrukerforbruker2 = System.out :: println; // Metodehenvisningen er kortere. forbruker2.accept ("Hei"); // Gi "Hallo" til lambdakroppen. Skriv ut Hello til standard utdata.

Det er ikke nødvendig å spesifisere en formell parameterliste for den forrige metodereferansen fordi kompilatoren kan utlede denne listen basert på Forbruker Denne parameteriserte typen java.lang.Streng faktisk type argument erstatter T i ugyldig godta (T t), og er også typen enkeltparameter i lambdakroppen System.out.println () metodeanrop.

Metodhenvisninger i dybden

EN metodehenvisning er en syntaktisk snarvei for å lage en lambda fra en eksisterende metode. I stedet for å gi et implementeringsorgan, refererer en metodehenvisning til en eksisterende klasses eller objektets metode. Som med en lambda, krever en metodehenvisning en måltype.

Du kan bruke referanser til å referere til klassens statiske metoder, bundne og ikke-bundne ikke-statiske metoder, og konstruktører. Du kan også bruke metodereferanser til å referere til forekomstmetoder i superklasse og gjeldende klassetyper. Jeg introduserer deg for hver av disse referansekategoriene og viser hvordan de brukes i en liten demo.

Lær mer om metodereferanser

Etter å ha lest denne delen, sjekk ut Metodehenvisninger i Java 8 (Toby Weston, februar 2014) for mer innsikt i metodereferanser i bundet og ubundet ikke-statisk metodesammenheng.

Henvisninger til statiske metoder

EN statisk metodehenvisning refererer til en statisk metode i en bestemt klasse. Dens syntaks er klassenavn::staticMethodName, hvor klassenavn identifiserer klassen og staticMethodName identifiserer den statiske metoden. Et eksempel er Heltall :: bitCount. Oppføring 1 viser en referanse for statisk metode.

Oppføring 1. MRDemo.java (versjon 1)

importere java.util.Arrays; importere java.util.function.Consumer; public class MRDemo {public static void main (String [] args) {int [] array = {10, 2, 19, 5, 17}; Forbrukerforbrukere = Arrays :: sort; consumer.accept (array); for (int i = 0; i <array.length; i ++) System.out.println (array [i]); System.out.println (); int [] array2 = {19, 5, 14, 3, 21, 4}; Forbrukerforbruker2 = (a) -> Arrays.sort (a); forbruker2.accept (array2); for (int i = 0; i <array2.length; i ++) System.out.println (array2 [i]); }}

Oppføring 1-er hoved() metoden sorterer et par heltall arrays via java.util.Arrayer klassen statisk tomromssortering (int [] a) metode, som vises i statisk referanse og tilsvarende lambdauttrykk-sammenhenger. Etter å ha sortert en matrise, a til loop skriver ut det sorterte matrisens innhold til standard utgangsstrøm.

Før vi kan bruke en metodereferanse eller en lambda, må den være bundet til et funksjonelt grensesnitt. Jeg bruker det forhåndsdefinerte Forbruker funksjonelt grensesnitt, som oppfyller metodereferansen / lambda-kravene. Sorteringsoperasjonen starter ved å sende matrisen som skal sorteres til Forbrukers aksepterer() metode.

Kompilere oppføring 1 (javac MRDemo.java) og kjør applikasjonen (java MRDemo). Du vil observere følgende utdata:

2 5 10 17 19 3 4 5 14 19 21

Henvisninger til bundne ikke-statiske metoder

EN bundet ikke-statisk metodehenvisning refererer til en ikke-statisk metode som er bundet til en mottaker gjenstand. Dens syntaks er objektnavn::instansMetodnavn, hvor objektnavn identifiserer mottakeren og instansMetodnavn identifiserer forekomstmetoden. Et eksempel er s :: trim. Oppføring 2 viser en bundet ikke-statisk metodehenvisning.

Oppføring 2. MRDemo.java (versjon 2)

importere java.util.function.Supplier; public class MRDemo {public static void main (String [] args) {String s = "Den raske brune reven hoppet over den late hunden"; utskrift (s :: lengde); skriv ut (() -> s.lengde ()); skriv ut (ny leverandør () {@ Override public Integer get () {return s.length (); // lukkes over s}}); } offentlig statisk tomtrykk (leverandørleverandør) {System.out.println (leverandør.get ()); }}

Oppføring 2-er hoved() metoden tilordner en streng til String variabel s og deretter påkaller skrive ut() klassemetode med funksjonalitet for å oppnå lengden på denne strengen som argument for denne metoden. skrive ut() påkalles i metodereferanse (s :: lengde -- lengde() er bundet til s), tilsvarende lambda og tilsvarende anonyme klassekontekster.

Jeg har definert skrive ut() å bruke java.util.function.Leverandør forhåndsdefinert funksjonelt grensesnitt, hvis få() metoden returnerer en leverandør av resultater. I dette tilfellet Leverandør forekomst sendt til skrive ut() implementerer sin få() metode for å returnere s.length (); skrive ut() gir ut denne lengden.

s :: lengde introduserer en lukking som lukkes s. Du kan se dette tydeligere i lambda-eksemplet. Fordi lambda ikke har noen argumenter, verdien av s er bare tilgjengelig fra vedlagte omfang. Derfor er lambdakroppen en lukking som lukker seg s. Det anonyme klasseeksemplet gjør dette enda tydeligere.

Compile Listing 2 og kjør applikasjonen. Du vil observere følgende utdata:

44 44 44

Henvisninger til ubundet ikke-statiske metoder

An ubundet ikke-statisk metodehenvisning refererer til en ikke-statisk metode som ikke er bundet til et mottakerobjekt. Dens syntaks er klassenavn::instanceMethodName, hvor klassenavn identifiserer klassen som deklarerer forekomstmetoden og instanceMethodName identifiserer forekomstmetoden. Et eksempel er String :: toLowerCase.

String :: toLowerCase er en ubundet ikke-statisk metodehenvisning som identifiserer den ikke-statiske String toLowerCase () metoden for String klasse. Imidlertid, fordi en ikke-statisk metode fortsatt krever et mottakerobjekt (i dette eksemplet a String objekt, som brukes til å påkalle toLowerCase () via metodehenvisningen), blir mottakerobjektet opprettet av den virtuelle maskinen. toLowerCase () vil bli påkalt på dette objektet. String :: toLowerCase spesifiserer en metode som tar en singel String argument, som er mottakerobjektet, og returnerer a String resultat. String :: toLowerCase () tilsvarer lambda (Streng s) -> {return s.toLowerCase (); }.

Oppføring 3 viser denne ubundet ikke-statiske metodehenvisningen.

Oppføring 3. MRDemo.java (versjon 3)

importere java.util.function.Function; public class MRDemo {public static void main (String [] args) {print (String :: toLowerCase, "STRING TO LOWERCASE"); print (s -> s.toLowerCase (), "STRING TO LOWERCASE"); skriv ut (ny funksjon () {@ Override public String gjelder (String s) // mottar argument i parameter s; {// trenger ikke å lukke over s return s.toLowerCase ();}}, "STRING TO LOWERCASE" ); } offentlig statisk tom utskrift (Funksjonsfunksjon, Streng s) {System.out.println (function.apply (s)); }}

Listing 3's hoved() metoden påkaller skrive ut() klassemetode med funksjonalitet for å konvertere en streng til små bokstaver og strengen som skal konverteres som metodens argumenter. skrive ut() påkalles i metodereferanse (String :: toLowerCase, hvor toLowerCase () er ikke bundet til et brukerdefinert objekt) og tilsvarende lambda og anonyme klassekontekster.

Jeg har definert skrive ut() å bruke java.util.function.Function forhåndsdefinert funksjonelt grensesnitt, som representerer en funksjon som godtar ett argument og produserer et resultat. I dette tilfellet Funksjon forekomst sendt til skrive ut() implementerer sin R gjelder (T t) metode for å returnere s.toLowerCase (); skrive ut() sender ut denne strengen.

Selv om String del av String :: toLowerCase får det til å se ut som om det refereres til en klasse, bare det refereres til en forekomst av denne klassen. Det anonyme klasseeksemplet gjør dette mer åpenbart. Merk at lambda mottar et argument i det anonyme klasseeksemplet; det lukkes ikke over parameteren s (dvs. det er ikke en lukking).

Kompilere oppføring 3 og kjør applikasjonen. Du vil observere følgende utdata:

streng til små bokstaver til små bokstaver til små bokstaver

Henvisninger til konstruktører

Du kan bruke en metodehenvisning for å henvise til en konstruktør uten å starte den valgte klassen. Denne typen metodehenvisning er kjent som en konstruktørreferanse. Dens syntaks er klassenavn::ny. klassenavn må støtte oppretting av objekter; det kan ikke nevne en abstrakt klasse eller et grensesnitt. Nøkkelord ny navngir den refererte konstruktøren. Her er noen eksempler:

  • Karakter :: ny: tilsvarer lambda (Character ch) -> new Character (ch)
  • Lang :: ny: tilsvarer lambda (lang verdi) -> ny Lang (verdi) eller (String s) -> nye Long (s)
  • ArrayList :: ny: tilsvarer lambda () -> ny ArrayList ()
  • flyte [] :: ny: tilsvarer lambda (int størrelse) -> ny flottør [størrelse]

Det siste referanseeksemplet til konstruktør spesifiserer en matritype i stedet for en klassetype, men prinsippet er det samme. Eksemplet viser en array konstruktør referanse til "konstruktøren" av en matritype.

Angi for å opprette en konstruktorreferanse ny uten konstruktør. Når en klasse som java.lang. lang erklærer flere konstruktører, sammenligner kompilatoren det funksjonelle grensesnittets type mot alle konstruktørene og velger den beste matchen. Oppføring 4 viser en konstruktørreferanse.

Oppføring 4. MRDemo.java (versjon 4)

importere java.util.function.Supplier; public class MRDemo {public static void main (String [] args) {Leverandørleverandør = MRDemo :: ny; System.out.println (leverandør.get ()); }}

Oppføring 4-er MRDemo :: ny konstruktørreferanse tilsvarer lambda () -> ny MRDemo (). Uttrykk provider.get () utfører denne lambdaen, som påberoper seg MRDemoer standard ikke-argument konstruktør og returnerer MRDemo objekt, som sendes til System.out.println (). Denne metoden konverterer objektet til en streng som den skriver ut.

Anta nå at du har en klasse med en konstruktør uten argument og en konstruktør som tar et argument, og at du vil ringe konstruktøren som tar et argument. Du kan utføre denne oppgaven ved å velge et annet funksjonelt grensesnitt, for eksempel det forhåndsdefinerte Funksjon grensesnittet vist i oppføring 5.

Oppføring 5. MRDemo.java (versjon 5)

importere java.util.function.Function; offentlig klasse MRDemo {privat strengnavn; MRDemo () {name = ""; } MRDemo (strengnavn) {this.name = name; System.out.printf ("MRDemo (strengnavn) kalt med% s% n", navn); } public static void main (String [] args) {Function function = MRDemo :: new; System.out.println (function.apply ("noe navn")); }}

Funksjonsfunksjon = MRDemo :: ny; får kompilatoren til å lete etter en konstruktør som tar en String argument, fordi Funksjons søke om() metoden krever en enkelt (i denne sammenheng) String argument. Utfører function.apply ("noe navn") resulterer i "noe navn" blir overført til MRDemo (strengnavn).

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