Что подразумевается под целевым типом лямбда и контекстом целевого типа в Java? - PullRequest
5 голосов
/ 17 марта 2019

Я читаю главу о лямбдах в «Java: полный справочник» Герберта Шильдта, и есть довольно много ссылок на «тип цели лямбда» и «контекст типа цели»:

Функциональный интерфейс определяет тип цели лямбда-выражение. Вот ключевой момент: лямбда-выражение может использоваться только в контексте, в котором указан тип цели .

Или:

Как упоминалось ранее, лямбда-выражение не выполняется само по себе. Скорее, он формирует реализацию абстрактного метода, определенного функциональный интерфейс, который определяет его тип цели . В результате лямбда Выражение может быть указано только в контексте, в котором тип цели определены. Один из этих контекстов создается, когда лямбда-выражение назначена ссылка на функциональный интерфейс. Другие контексты целевого типа включить инициализацию переменной, операторы возврата и аргументы метода, чтобы назвать несколько.

Еще один:

Функциональный интерфейс, связанный с лямбда-выражением, может быть универсальным. В в этом случае целевой тип лямбда-выражения частично определяется аргументом типа или аргументами, указанными при функциональном интерфейсе ссылка объявлена.

Может кто-нибудь помочь мне понять, что означает лямбда-тип цели?

Например, в (int n) -> n % 2 == 0 есть int тип цели лямбды?

или в:

interface MyInterface<T> {
    T func();
}

MyInterface<String> myInt = () -> { return "123"; }

Каков целевой тип лямбды? Это String или MyInterface<String>? И какой здесь контекст лямбды?

Я прочитал несколько сообщений на SO по этой теме, но все еще не могу полностью понять понятия.

Спасибо.

Ответы [ 3 ]

6 голосов
/ 17 марта 2019

Одним из определений «цели» (взято из здесь ) является:

результат или ситуация, которую вы намереваетесь достичь.

Можно сказать, что результатом, которое намеревается достичь лямбда-выражение, является реализация некоторого функционального интерфейса.Следовательно, этот функциональный интерфейс может рассматриваться как цель этого лямбда-выражения, а тип функционального интерфейса - это тип цели.

Следовательно, тип цели - это тип функционального интерфейса, реализуемого лямбда-выражением.

Тип цели может быть выведен на основе контекста , в котором используется лямбда-выражение:

  1. Если лямбда-выражение назначено ссылке на функциональный интерфейспеременная, типом этой переменной является целевой тип.
  2. Если лямбда-выражение возвращается каким-либо методом, возвращаемый тип метода - целевой тип.
  3. Если лямбда-выражение равнопереданный в качестве аргумента методу, тип соответствующего аргумента, ожидаемого методом, является типом цели.

В

(int n) -> n % 2 == 0

тип цели неизвестен.Если вы назначите это выражение для некоторой ссылки на функциональный интерфейс, это будет тип цели.

В

MyInterface<String> myInt = () -> { return "123"; }

тип цели - MyInterface<String>.

3 голосов
/ 17 марта 2019

Вы должны понимать «тип цели» как функциональный интерфейс, для которого используется (предназначена) функция.

Подумайте об этом: что это за лямбда-выражение и как его можно использовать?

() -> "123";

Как отмечается в книге, это выражение нельзя использовать само по себе. Это должно быть связано с функциональным интерфейсом.

Теперь, какой функциональный интерфейс может быть типом лямбда-выражения, взят из контекста. Вот где «целевой тип» лямбда-выражения что-то значит.

Рассмотрим эти примеры:

Пример 1:

void printString(Supplier<String> supplier) {
    System.out.println(supplier.get());
}

Вы можете позвонить с помощью

printString(() -> "123");

В данном случае вы подразумеваете тип () -> "123" как Supplier<String>. Это тип цели () -> "123" в этом контексте.

Пример 2:

MyInterface<String> myInt = () -> "123";

Как видите, использовалось идентичное лямбда-выражение, но его тип цели теперь MyInterface<String>.

Аналогично, вы можете объявить другой функциональный интерфейс с такой же сигнатурой, что и MyInterface.func(), и назначить ему то же самое лямбда-выражение. Тип цели изменяется в этих различных контекстах.

0 голосов
/ 17 марта 2019

Я решил прочитать немного больше о 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.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...