::
wird als Methodenreferenz bezeichnet. Es handelt sich im Grunde um einen Verweis auf eine einzelne Methode, d. h. es bezieht sich auf eine vorhandene Methode nach ihrem Namen.
Kurze Erklärung:
Hier ist ein Beispiel für einen Verweis auf eine statische Methode:
class Hey {
public static double quadrat(double num){
return Math.pow(num, 2);
}
}
Function quadrat = Hey::quadrat;
double antwort = quadrat.apply(23d);
quadrat
kann genauso wie Objektverweise weitergereicht und bei Bedarf ausgelöst werden. Tatsächlich kann es genauso einfach verwendet werden wie ein Verweis auf "normale" Methoden von Objekten wie auf statische
. Zum Beispiel:
class Hey {
public double quadrat(double num) {
return Math.pow(num, 2);
}
}
Hey hey = new Hey();
Function quadrat = hey::quadrat;
double antwort = quadrat.apply(23d);
Function
oben ist eine funktionale Schnittstelle. Um ::
vollständig zu verstehen, ist es wichtig, funktionale Schnittstellen ebenfalls zu verstehen. Einfach ausgedrückt ist eine funktionale Schnittstelle eine Schnittstelle mit nur einer abstrakten Methode.
Beispiele für funktionale Schnittstellen sind Runnable
, Callable
und ActionListener
.
Function
oben ist eine funktionale Schnittstelle mit nur einer Methode: apply
. Sie nimmt ein Argument entgegen und liefert ein Ergebnis.
Der Grund, warum ::
so großartig sind, ist dies:
Methodenreferenzen sind Ausdrücke, welche genauso behandelt werden wie Lambda-Ausdrücke (...), jedoch anstelle eines Methodenrumpfes bereits existierende Methoden beim Namen nennen.
Z. B. anstatt den Lambda-Körper zu schreiben
Function quadrat = (Double x) -> x * x;
Kannst du einfach das tun
Function quadrat = Hey::quadrat;
Zur Laufzeit verhalten sich diese beiden quadrat
Methoden genau gleich. Der Bytecode kann der gleiche sein oder auch nicht (obwohl, für den obigen Fall, der gleiche Bytecode erzeugt wird; kompiliere das obige und überprüfe mit javap -c
).
Das einzige Hauptkriterium, das erfüllt werden muss, ist: Die von dir bereitgestellte Methode sollte eine ähnliche Signatur wie die Methode der funktionalen Schnittstelle haben, die du als Objektverweis verwendest.
Folgendes ist illegal:
Supplier p = Hey::quadrat; // illegal
quadrat
erwartet ein Argument und gibt eine double
zurück. Die Methode get
in Supplier gibt einen Wert zurück, nimmt aber kein Argument an. Daher ergibt dies einen Fehler.
Ein Methodenverweis bezieht sich auf die Methode einer funktionalen Schnittstelle. (Wie erwähnt, können funktionale Schnittstellen jeweils nur eine Methode haben.)
Weitere Beispiele: Die Methode accept
in Consumer nimmt eine Eingabe entgegen, gibt aber nichts zurück.
Consumer b1 = System::exit; // void exit(int status)
Consumer b2 = Arrays::sort; // void sort(Object[] a)
Consumer b3 = MyProgram::main; // void main(String... args)
class Hey {
public double getRandom() {
return Math.random();
}
}
Callable call = hey::getRandom;
Supplier call2 = hey::getRandom;
DoubleSupplier sup = hey::getRandom;
// Supplier ist eine funktionale Schnittstelle, die kein Argument annimmt und ein Ergebnis liefert
Oben nimmt getRandom
kein Argument an und gibt eine double
zurück. Daher kann jede funktionale Schnittstelle, die die Kriterien erfüllt: kein Argument entgegennehmen und double
zurückgeben, verwendet werden.
Ein weiteres Beispiel:
Set set = new HashSet<>();
set.addAll(Arrays.asList("leo","bale","hanks"));
Predicate pred = set::contains;
boolean exists = pred.test("leo");
Bei parametrisierten Typen:
class Param {
T elem;
public T get() {
return elem;
}
public void set(T elem) {
this.elem = elem;
}
public static E returnSame(E elem) {
return elem;
}
}
Supplier> obj = Param::new;
Param param = obj.get();
Consumer c = param::set;
Supplier s = param::get;
Function func = Param::returnSame;
Methodenverweise können unterschiedliche Stile haben, aber grundsätzlich bedeuten sie alle dasselbe und können einfach als Lambdas visualisiert werden:
- Eine statische Methode (
Klassenname::methName
)
- Eine Instanzmethode eines bestimmten Objekts (
instanceRef::methName
)
- Eine Supermethode eines bestimmten Objekts (
super::methName
)
- Eine Instanzmethode eines beliebigen Objekts eines bestimmten Typs (
Klassenname::methName
)
- Ein Klassenkonstruktorverweis (
Klassenname::new
)
- Ein Array-Konstruktorverweis (
TypNAME[]::new
)
Zur weiteren Referenz siehe State of the Lambda.