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) );
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));
}
});
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 s
parametern:
list.forEach( (s) -> System.out.println(s) );
list.forEach( s -> System.out.println(s) );
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 );
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 this
och super
variablerna 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::method
x -> 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);
GO TO FULL VERSION