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