Що таке "тип SAM" на Java?


133

Читаючи специфікацію Java-8, я постійно бачу посилання на "типи SAM". Я не зміг знайти чіткого пояснення, що це таке.

Що таке тип SAM і що є прикладним сценарієм, коли його можна використовувати?


4
Див. Cr.openjdk.java.net/~briangoetz/lambda/lambda-state-3.html (який я знайшов після одного пошуку, який переніс мене до іншого питання SO).
Джон Скіт

2
Будь ласка, не посилайте людей на застарілу інформацію. Трохи довший пошук переніс би вас до більш сучасної версії: cr.openjdk.java.net/~briangoetz/lambda/lambda-state-4.html , якій самому виповнилося 18 місяців і в багатьох застаріло. місця. На питання ОП відповідає відповідь на lambdafaq.org/what-is-a-functional-interface , одна сторінка в FAQ, яку я намагаюсь бути в курсі того, що було донедавна швидко змінюється мовою та розробкою API.
Моріс Нафталін

1
@MauriceNaftalin Можна також просто зв’язати слід Java , який постійно оновлює команда розробників.
Брайан

1
Поточний термін - "функціональний інтерфейс".
newacct

1
@MauriceNaftalin: Вибачте, пропустив цю. Звичайно, я б з цим пов’язав, якби це знайшов.
Джон Скіт

Відповіді:


142

Резюмуючи посилання 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, щоб побачити поточний синтаксис.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.