Читаючи специфікацію 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, щоб побачити поточний синтаксис.