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ć?
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ć?
Odpowiedzi:
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
, Callable
itp 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ć Callable
using 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 Comparator
przy 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 invokedynamic
instrukcji kodu bajtowego Javy dodaną 7. powiedziałem wcześniej, że deklarując lambda tworzy instancję Callable
lub Comparable
podobny do anonimowej klasy, ale nie jest to absolutnie prawdziwe. Zamiast tego, przy pierwszym invokedynamic
wywołaniu, tworzy on procedurę obsługi funkcji Lambda za pomocą LambdaMetafactory.metafactory
metody , 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ę.