Was ist ein 'SAM-Typ' in Java?

132

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?

Cody
quelle
4
Siehe cr.openjdk.java.net/~briangoetz/lambda/lambda-state-3.html (das ich nach einer einzelnen Suche gefunden habe, die mich zu einer anderen SO-Frage führte).
Jon Skeet
2
Bitte verweisen Sie Personen nicht auf veraltete Informationen. Eine etwas längere Suche hätte Sie zu der aktuelleren Version geführt: cr.openjdk.java.net/~briangoetz/lambda/lambda-state-4.html , die selbst 18 Monate alt und in vielen Fällen veraltet ist setzt. Die Frage des OP wird unter lambdafaq.org/what-is-a-functional-interface beantwortet , einer Seite in einer FAQ, die ich in einer bis vor kurzem sich schnell ändernden Sprach- und API-Entwicklung auf dem neuesten Stand zu halten versuche.
Maurice Naftalin
1
@MauriceNaftalin Sie können auch einfach den Java-Trail verknüpfen , der vom Entwicklungsteam auf dem neuesten Stand gehalten wird.
Brian
1
Der aktuelle Begriff lautet "Funktionsschnittstelle".
Newacct
1
@ MauriceNaftalin: Entschuldigung, habe das verpasst. Hätte sicherlich damit zu tun gehabt, wenn ich das gefunden hätte.
Jon Skeet

Antworten:

141

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, Callableusw. 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 CallableLambda-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 Comparatorbestimmte 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 invokedynamicin Java 7 hinzugefügten Bytecode-Anweisung. Ich habe bereits gesagt, dass durch das Deklarieren eines Lambda eine Instanz einer anonymen Klasse Callableoder einer Comparableähnlichen Klasse erstellt wird, dies ist jedoch nicht unbedingt der Fall. Stattdessen wird beim ersten Aufruf von invokedynamicein Lambda-Funktionshandler mithilfe der LambdaMetafactory.metafactoryMethode 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.

Brian
quelle
Sind Sie sicher #{ "Hello world!" };, dass die Syntax korrekt ist? Ich dachte, man würde es in einem Lambda-Ausdruck wie ausdrücken () -> "Hello world!".
Asgs
Ah ja. Ich habe gerade die Kommentare von @ Maurice gelesen. Sie sollten diesen Beitrag bearbeiten, um die neuesten Informationen wiederzugeben. Gut zu wissen, dass #{}Lambdas zuvor so vertreten waren.
Asgs
1
@asgs Ich hatte unten einen kleinen Klappentext, der auf den Java-Trail zeigte, aber ich habe die Antwort trotzdem aktualisiert, um die aktuelle Syntax zu verwenden.
Brian