Co to jest „typ SAM” w Javie?

133

Czytając specyfikację Java-8, wciąż widzę odniesienia do „typów SAM”. Nie udało mi się znaleźć jasnego wyjaśnienia, co to jest.

Co to jest typ SAM i jaki jest przykładowy scenariusz, kiedy można go użyć?

Cody
źródło
4
Zobacz cr.openjdk.java.net/~briangoetz/lambda/lambda-state-3.html (które znalazłem po jednym wyszukiwaniu, które doprowadziło mnie do innego pytania SO).
Jon Skeet
2
Nie odsyłaj ludzi do nieaktualnych informacji. Nieco dłuższe wyszukiwanie doprowadziłoby Cię do bardziej aktualnej wersji: cr.openjdk.java.net/~briangoetz/lambda/lambda-state-4.html , która ma już 18 miesięcy i jest obecnie nieaktualna w wielu miejsc. Odpowiedź na pytanie OP znajduje się na lambdafaq.org/what-is-a-functional-interface , jednej stronie w FAQ, na której staram się być na bieżąco z tym, co do niedawna było szybko zmieniającym się językiem i rozwojem API.
Maurice Naftalin
1
@MauriceNaftalin Możesz także po prostu połączyć ścieżkę Java , która jest aktualizowana przez zespół programistów.
Brian
1
Obecny termin to „interfejs funkcjonalny”.
newacct
1
@MauriceNaftalin: Przepraszam, przegapiłem to. Z pewnością połączyłbym się z tym, gdybym to znalazł.
Jon Skeet

Odpowiedzi:

142

Podsumowując link Jon zamieszczonych 1 w przypadku, gdy kiedykolwiek idzie w dół, „SAM” jest skrótem od „jednej metody abstrakcyjnej” i „SAM-type” odnosi się do takich interfejsów Runnable, Callableitp lambda wyrażeń, nowej funkcji w Java 8 są uważane za typ SAM i można je dowolnie konwertować na nie.

Na przykład z takim interfejsem:

public interface Callable<T> {
    public T call();
}

Możesz zadeklarować Callableusing wyrażenia lambda w następujący sposób:

Callable<String> strCallable = () -> "Hello world!";
System.out.println(strCallable.call()); // prints "Hello world!"

Wyrażenia lambda w tym kontekście to przeważnie cukier syntaktyczny. Wyglądają lepiej w kodzie niż klasy anonimowe i są mniej restrykcyjne w nazewnictwie metod. Weź ten przykład z linku:

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);

Tworzy to Comparatorprzy użyciu określonej metody, która nie ma tej samej nazwy co Comparator.compare, dzięki czemu nie musisz stosować nazewnictwa metod w interfejsie i możesz mieć wiele nadpisań porównania w klasie, a następnie tworzyć komparatory w locie za pośrednictwem wyrażenia lambda.

Idąc głębiej ...

Na głębszym poziomie, te narzędzia Java z wykorzystaniem invokedynamicinstrukcji kodu bajtowego Javy dodaną 7. powiedziałem wcześniej, że deklarując lambda tworzy instancję Callablelub Comparablepodobny do anonimowej klasy, ale nie jest to absolutnie prawdziwe. Zamiast tego, przy pierwszym invokedynamicwywołaniu, tworzy on procedurę obsługi funkcji Lambda za pomocą LambdaMetafactory.metafactorymetody , a następnie używa tej buforowanej instancji w przyszłych wywołaniach Lambda. Więcej informacji można znaleźć w tej odpowiedzi .

To podejście jest złożone i obejmuje nawet kod, który może odczytywać wartości pierwotne i referencje bezpośrednio z pamięci stosu, aby przekazać je do kodu Lambda (np. Aby obejść konieczność przydzielenia Object[]tablicy do wywołania Lambdy), ale umożliwia przyszłe iteracje implementacji Lambda aby zastąpić stare implementacje bez martwienia się o zgodność kodu bajtowego. Jeśli inżynierowie w Oracle zmienią podstawową implementację Lambda w nowszej wersji JVM, Lambdas skompilowane na starszej JVM automatycznie użyje nowszej implementacji bez żadnych zmian ze strony programisty.


1 Składnia odsyłacza jest nieaktualna. Spójrz na Lambda Expressions Java Trail, aby zobaczyć aktualną składnię.

Brian
źródło