1. Funktionsmetoder

Om ett gränssnitt bara har en metod kan en variabel av den gränssnittstypen tilldelas ett värde som ges av ett lambda-uttryck (lambda-funktion). Sådana gränssnitt blev kända som funktionella gränssnitt (efter att Java lagt till stöd för lambda-funktioner).

Till exempel har Java gränssnittet Consumer<Type>, som har accept(Type obj)metoden. Varför behövs detta gränssnitt?

I Java 8 har samlingar en forEach()metod som låter dig utföra vissa åtgärder för varje element i samlingen . Och här Consumer<T>används det funktionella gränssnittet för att överföra handlingen till forEach()metoden.

Så här kan du visa alla element i en samling :

ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "Hello", "how's", "life?");

list.forEach( (s) -> System.out.println(s) );
Visar alla element i en samling (med ett lambda-uttryck)

Kompilatorn konverterar koden ovan till koden nedan:

ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "Hello", "how's", "life?");

list.forEach(new Consumer<String>()
{
   public void accept(String s)
   {
      System.out.println(s));
   }
});
Visa alla element i en samling (med en anonym klass)

Den första versionen är definitivt kortare än den andra. Och medan kod med lambda-uttryck är svår att läsa, är kod med anonyma inre klasser ibland ännu svårare att läsa.



2. Metodreferens

Vår lambdauttryckskod kan dock skrivas ännu kortare.

Först kan du utelämna parentesen runt sparametern:

list.forEach( (s) -> System.out.println(s) );
Innan
list.forEach( s -> System.out.println(s) );
Efter

Detta kan endast göras om det finns en parameter . Om det finns flera parametrar måste du använda parenteser .

Och för det andra kan du skriva det så här:

list.forEach( System.out::println );
Mest kompakt notation

Detta är exakt samma notation. Observera att det inte finns några parenteser efter println.

Här har vi samma kod - ett metodanrop:

object::method
x -> object.method(x)

Tänk på det: vi ville utföra några åtgärder för varje element i samlingen list. Om åtgärden är ett enda funktionsanrop (som t.ex. println()), är det vettigt att helt enkelt skicka funktionen till metoden som en parameter.

Men hur förklarar vi för kompilatorn att vi vill skicka metoden istället för att kalla den? För att göra detta, istället för punktoperatorn, använder vi två kolon före metodnamnet. Ett enda kolon används redan för att indikera den ternära operatorn.

Detta är den enklaste och mest kompakta notationen.



3. Konstruktör

Metodreferenser med dubbla kolon är väldigt praktiska när vi arbetar med I/O-strömmar. Du kommer att se detta lite senare.

Under tiden, låt oss prata om tre populära sätt att skicka en metodreferens:

Referens till en metod för ett objekt

För att skicka en referens till en metod för ett objekt måste du skriva något som . Denna kod motsvarar .object::method
x -> object.method(x)

Specialen thisoch supervariablerna kan användas som objekt.

Referens till en metod för en klass

För att skicka en referens till en statisk metod måste du skriva något som . Denna kod konverteras till kod somclass::methodx -> class.method(x);

Referens till en konstruktör

En konstruktor beter sig på samma sätt som en statisk klassmetod, så du kan också skicka en referens till en konstruktor. Så här ser det ut: .class::new

Till exempel kan du komma runt typradering för samlingar och skicka toArray()metoden en referens till en konstruktor som skapar den önskade arrayen:toArray(int[]::new);