Я решил прочитать немного больше о lamdas и нашел отличную книгу Кишори Ширана "Начало языковых особенностей Java 8: Лямбда-выражения, внутренние классы, потоки, ввод-вывод, коллекции и потоки".
Я просто приведу несколько абзацев:
Каждое выражение в Java имеет тип; так же как и лямбда-выражение. Тип лямбда-выражения является типом функционального интерфейса. Когда вызывается абстрактный метод функционального интерфейса, выполняется тело лямбда-выражения.
Рассмотрим лямбда-выражение, которое принимает параметр String и возвращает его длину:
(String str) -> str.length()
Каков тип этого лямбда-выражения? Ответ в том, что мы не знаем. Глядя на лямбда-выражение, все, что вы можете сказать, это то, что он принимает параметр String и возвращает int, который является длиной String. Его тип может быть любым типом функционального интерфейса с абстрактным методом, который принимает String в качестве параметра и возвращает int. Ниже приведен пример такого функционального интерфейса:
@FunctionalInterface
interface StringToIntMapper {
int map(String str);
}
Лямбда-выражение представляет собой экземпляр функционального интерфейса StringToIntMapper
, когда он появляется в операторе присваивания, например:
StringToIntMapper mapper = (String str) -> str.length();
В этом операторе компилятор находит, что правая часть оператора присваивания является лямбда-выражением. Чтобы вывести его тип, он смотрит на левую часть оператора присваивания, который ожидает экземпляр интерфейса StringToIntMapper
; он проверяет, что лямбда-выражение соответствует объявлению метода map()
в интерфейсе StringToIntMapper
; наконец, из этого следует, что типом лямбда-выражения является тип интерфейса StringToIntMapper
.
Это лямбда-выражение может иметь различные типы функциональных интерфейсов в зависимости от контекста, в котором оно используется. В Java существует два типа выражений - автономные выражения и поли-выражения
Автономное выражение - это выражение, тип которого может быть определен выражением без знания контекста его использования. Выражение поли - это выражение, которое имеет разные типы в разных контекстах. Компилятор определяет тип выражения. Контексты, которые позволяют использовать поли-выражения, известны как поли-контексты. Все лямбда-выражения в Java являются поли-выражениями. Вы должны использовать его в контексте, чтобы знать его тип. Поли-выражения существовали в Java до Java 8 и лямбда-выражений. Например, выражение new ArrayList<>()
является поли-выражением. Вы
не может определить его тип, если вы не укажете контекст его использования.
Компилятор определяет тип лямбда-выражения. Контекст, в котором используется лямбда-выражение, ожидает тип, который называется целевым типом. Процесс вывода типа лямбда-выражения из контекста называется типизацией цели. Рассмотрим следующий псевдокод для оператора присваивания, в котором переменной типа T
назначено лямбда-выражение:
T t = <LambdaExpression>;
Целевой тип лямбда-выражения в этом контексте - T
. Компилятор использует следующие правила, чтобы определить, совместимо ли присвоение <LambdaExpression>
с его целевым типом T
:
T
должен быть функциональным интерфейсом.
- Лямбда-выражение имеет тот же номер и тип параметров, что и абстрактный метод
T
. Для неявного лямбда-выражения компилятор выведет типы параметров из абстрактного метода T
.
- Типом возвращаемого значения из тела лямбда-выражения является присваивание, совместимое с типом возврата абстрактного метода
T
. - Если тело лямбда-выражения выбрасывает какие-либо проверенные исключения, эти исключения должны быть совместимы с объявленным выражением throws абстрактного метода
T
. Ошибка времени компиляции - выбрасывать отмеченные исключения из тела лямбда-выражения, если метод целевого типа не содержит предложение throws.