Beim Lesen der Java-8-Spezifikation sehe ich immer wieder Verweise auf 'SAM-Typen'. Ich konnte keine klare Erklärung dafür finden.
Was ist ein SAM-Typ und was ist ein Beispielszenario dafür, wann einer verwendet werden könnte?
Beim Lesen der Java-8-Spezifikation sehe ich immer wieder Verweise auf 'SAM-Typen'. Ich konnte keine klare Erklärung dafür finden.
Was ist ein SAM-Typ und was ist ein Beispielszenario dafür, wann einer verwendet werden könnte?
Antworten:
Zusammengefasst den Link Jon geschrieben 1 , falls es jemals untergeht, „SAM“ steht für „Single abstrakte Methode“ und „SAM-Typ“ bezieht sich auf Schnittstellen wie Runnable
, Callable
usw. Lambda - Ausdrücke, ein neues Feature in Java 8 sind wird als SAM-Typ betrachtet und kann frei in diese konvertiert werden.
Zum Beispiel mit einer Schnittstelle wie dieser:
public interface Callable<T> {
public T call();
}
Sie können einen Callable
Lambda-Ausdruck wie folgt deklarieren :
Callable<String> strCallable = () -> "Hello world!";
System.out.println(strCallable.call()); // prints "Hello world!"
Lambda-Ausdrücke sind in diesem Zusammenhang meist nur syntaktischer Zucker. Sie sehen im Code besser aus als anonyme Klassen und sind weniger restriktiv bei der Benennung von Methoden. Nehmen Sie dieses Beispiel aus dem Link:
class Person {
private final String name;
private final int age;
public static int compareByAge(Person a, Person b) { ... }
public static int compareByName(Person a, Person b) { ... }
}
Person[] people = ...
Arrays.sort(people, Person::compareByAge);
Auf diese Weise wird eine Comparator
bestimmte Methode verwendet, die nicht denselben Namen hat wie Comparator.compare
. Auf diese Weise müssen Sie nicht der Benennung der Schnittstellen von Methoden folgen, und Sie können mehrere Vergleichsüberschreibungen in einer Klasse vornehmen und dann die Komparatoren im laufenden Betrieb über erstellen die Lambda-Ausdrücke.
Tiefer gehen ...
Auf einer tieferen Ebene implementiert Java diese mithilfe der invokedynamic
in Java 7 hinzugefügten Bytecode-Anweisung. Ich habe bereits gesagt, dass durch das Deklarieren eines Lambda eine Instanz einer anonymen Klasse Callable
oder einer Comparable
ähnlichen Klasse erstellt wird, dies ist jedoch nicht unbedingt der Fall. Stattdessen wird beim ersten Aufruf von invokedynamic
ein Lambda-Funktionshandler mithilfe der LambdaMetafactory.metafactory
Methode erstellt und diese zwischengespeicherte Instanz in zukünftigen Aufrufen des Lambda verwendet. Weitere Informationen finden Sie in dieser Antwort .
Dieser Ansatz ist komplex und umfasst sogar Code, der primitive Werte und Referenzen direkt aus dem Stapelspeicher lesen kann, um ihn in Ihren Lambda-Code zu übertragen (z. B. um ein Object[]
Array zum Aufrufen Ihres Lambda zuzuweisen ), ermöglicht jedoch zukünftige Iterationen der Lambda-Implementierung um alte Implementierungen zu ersetzen, ohne sich um die Bytecode-Kompatibilität sorgen zu müssen. Wenn die Ingenieure von Oracle die zugrunde liegende Lambda-Implementierung in einer neueren Version der JVM ändern, verwendet Lambdas, das auf einer älteren JVM kompiliert wurde, automatisch die neuere Implementierung, ohne dass der Entwickler Änderungen vornimmt.
1 Die Syntax des Links ist veraltet. Schauen Sie sich den Lambda Expressions Java Trail an, um die aktuelle Syntax zu sehen.
#{ "Hello world!" };
, dass die Syntax korrekt ist? Ich dachte, man würde es in einem Lambda-Ausdruck wie ausdrücken () -> "Hello world!"
.
#{}
Lambdas zuvor so vertreten waren.