Читаючи специфікацію Java-8, я постійно бачу посилання на "типи SAM". Я не зміг знайти чіткого пояснення, що це таке.
Що таке тип SAM і що є прикладним сценарієм, коли його можна використовувати?
Читаючи специфікацію Java-8, я постійно бачу посилання на "типи SAM". Я не зміг знайти чіткого пояснення, що це таке.
Що таке тип SAM і що є прикладним сценарієм, коли його можна використовувати?
Відповіді:
Резюмуючи посилання Jon відправив 1 в разі, коли йде вниз, «SAM» означає «єдиний абстрактний метод» і «SAM-тип» відноситься до інтерфейсів , як Runnable, Callableі т.д. лямбда - вираження, нова функція в Java 8, є вважається типом SAM і може вільно перетворюватися на них.
Наприклад, з таким інтерфейсом:
public interface Callable<T> {
public T call();
}
Ви можете оголосити Callableвикористовуючи лямбда-вирази на зразок цього:
Callable<String> strCallable = () -> "Hello world!";
System.out.println(strCallable.call()); // prints "Hello world!"
Ламбда-вирази в цьому контексті - це здебільшого лише синтаксичний цукор. Вони виглядають краще в коді, ніж анонімні класи, і менш обмежують імена методів. Візьміть цей приклад за посиланням:
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);
Це створює Comparatorвикористання специфічного методу, який не має того самого імені, тому Comparator.compareщо вам не доведеться дотримуватися методів іменування інтерфейсу, і ви можете мати кілька варіантів порівняння в класі, а потім створювати компаратори на ходу через лямбдаські вирази.
Йдемо глибше ...
На більш глибокому рівні, Java реалізує ці використовуючи invokedynamicінструкцію байткода доданий в Java 7. Я сказав раніше , що оголошуючи Lambda створює екземпляр Callableабо Comparableсхожий на анонімне клас, але це не строго вірно. Натомість, перший раз, коли invokedynamicвикликається, він створює обробник функції Lambda за допомогою LambdaMetafactory.metafactoryметоду , потім використовує цей кешований екземпляр у майбутніх викликах Lambda. Більше інформації можна знайти у цій відповіді .
Цей підхід є складним і навіть включає код, який може читати примітивні значення та посилання безпосередньо з пам'яті стека, щоб перейти у ваш код лямбда (наприклад, щоб обійти необхідність виділити Object[]масив для виклику вашої лямбда), але він дозволяє надалі ітерації реалізації Lambda замінити старі реалізації, не турбуючись про сумісність байт-кодів. Якщо інженери в Oracle поміняють базову реалізацію Lambda на більш нову версію JVM, Lambdas, складений на більш старій версії JVM, автоматично використовуватиме нову реалізацію без будь-яких змін з боку розробника.
1 Синтаксис посилання застарів. Подивіться на Java Trail Lambda Expressions, щоб побачити поточний синтаксис.