Чтение спецификации Java-8, я продолжаю видеть ссылки на "типы SAM". Я не смог найти четкое объяснение того, что это такое.
Что такое тип SAM и каков примерный сценарий использования одного из них?
Чтение спецификации Java-8, я продолжаю видеть ссылки на "типы SAM". Я не смог найти четкое объяснение того, что это такое.
Что такое тип SAM и каков примерный сценарий использования одного из них?
Чтобы суммировать ссылку, которую Джон опубликовал 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
метода LambdaMetafactory.metafactory
, а затем использует этот кэшированный экземпляр в будущих вызовах Lambda. Больше информации можно найти в этом ответе.
Этот подход сложен и даже включает в себя код, который может считывать примитивные значения и ссылки непосредственно из стековой памяти для передачи в ваш лямбда-код (например, для обхода необходимости выделять массив Object[]
для вызова вашей лямбды), но он допускает будущие итерации реализация Lambda, чтобы заменить старые реализации, не беспокоясь о совместимости байт-кода. Если инженеры Oracle изменят базовую реализацию Lambda в более новой версии JVM, Lambdas, скомпилированный на более старой JVM, будет автоматически использовать более новую реализацию без каких-либо изменений со стороны разработчика.
1 Синтаксис ссылки устарел. Взгляните на Java Trail Lambda Expressions, чтобы увидеть текущий синтаксис.
Интерфейсы, имеющие только один абстрактный метод, известный как Интерфейсы с одним абстрактным методом (SAM Interfaces).
Обычно они называются функциональными интерфейсами.
Вы можете прочитать больше на Что такое интерфейс SAM?