Java Lambdas и закрытие
Я слышал, что lambdas скоро появятся на Java рядом с вами (J8). Я нашел пример того, как они будут выглядеть на каком-то блоге:
SoccerService soccerService = (teamA, teamB) -> {
SoccerResult result = null;
if (teamA == teamB) {
result = SoccerResult.DRAW;
}
else if(teamA < teamB) {
result = SoccerResult.LOST;
}
else {
result = SoccerResult.WON;
}
return result;
};
Итак, прямо с места в карьер:
- Где указаны
teamA
и teamB
? Или они (как какая-то странная форма дженериков)?
- Является ли лямбда типом закрытия, или это наоборот?
- Какие преимущества это даст мне типичную анонимную функцию?
Ответы
Ответ 1
Выражение Lambda - это просто синтаксический сахар для реализации целевого интерфейса, это означает, что вы реализуете конкретный метод в интерфейсе посредством выражения лямбда. Компилятор может вывести типы параметров в интерфейсе и почему вам не нужно явно определять их в выражении лямбда.
Например:
Comparator<String> c = (s1, s2) -> s1.compareToIgnoreCase(s2);
В этом выражении выражение лямбда очевидно реализует строки Comparator
, поэтому это означает, что лямбда-выражение является синтаксическим сахаром для реализации compare(String, String)
.
Таким образом, компилятор может с уверенностью предположить тип s1
и s2
is String
.
Тип вашего целевого интерфейса предоставляет всю информацию, которую компилятор должен определить, каковы фактические типы параметров лямбда.
Брайант Гетц, разработчик Java Language Architect в Oracle Corportion, опубликовал пару статей, посвященных работе JDK 8 Lambdas. Я считаю, что ответы на ваши вопросы есть:
Эта вторая статья объясняет, как лямбда-выражения реализуются на уровне байт-кода и могут помочь вам углубиться в детали вашего второго вопроса.
Ответ 2
См. эту страницу для полной версии этого примера (однако соответствующие разделы показаны ниже).
Типы выводятся из интерфейса SoccerService и перечисления SoccerResult, не показаны в вашем фрагменте:
enum SoccerResult{
WON, LOST, DRAW
}
interface SoccerService {
SoccerResult getSoccerResult(Integer teamA, Integer teamB);
}
Выгода (от lambdas по сравнению с обычными анонимными классами) - это просто сокращенная многословность:
(x, y) => x + y
против чего-то вроде:
new Adder()
{
public int add(int x, int y)
{
return x + y;
}
}
Для разницы между замыканием и лямбдой см. этот вопрос.
Ответ 3
- Где введена командаA и teamB? Или они (как какая-то странная форма дженериков)?
Использовать целевую типизацию Lambda, так же как вызовы общих методов (начиная с 1.5) и алмазный [не an] оператор (начиная с 1.7). Грубо говоря, где тип, к которому применяется результат, указан (или может быть выведен), который используется для обеспечения типа базового типа Single Abstract Method (SAM) и, следовательно, типов параметров метода.
В качестве примера вывода общего метода в 1.5:
Set<Team> noTeams = Collections.emptySet();
И оператор алмаза в 1.7:
Set<Team> aTeams = new HashSet<>();
Команда, команда, команда, команда, команда, команда. Мне даже нравится говорить слово команда.
- Является ли лямбда типом закрытия, или это наоборот?
Лямбда - ограниченная форма закрытия почти так же, как анонимные внутренние классы, но с некоторыми случайными различиями, чтобы выгнать вас:
Внешний this
не скрывается внутренней this
. Это означает, что тот же текст в лямбде и анонимном внутреннем классе может означать тонко, но совершенно разные вещи. Это должно держать Qaru занятым странными вопросами.
Чтобы восполнить недостаток внутренней this
, если она назначена непосредственно локальной переменной, это значение доступно в пределах лямбда. IIRC (я мог бы проверить, но не привык), в анонимном внутреннем классе локальный будет в области видимости и скрыть переменные во внешнем пространстве, но вы не сможете его использовать. Я считаю, что отсутствие инициализатора экземпляра делает это намного проще.
Локальные поля, которые не помечены final
, но могут быть обработаны так, как будто они final
. Таким образом, они не входят в сферу действия, но вы можете читать (хотя и не писать) их.
- Какие преимущества это даст мне типичную анонимную функцию?
Брачный синтаксис. Что об этом.
Конечно, остальная часть синтаксиса Java так же безнадежно плоха, как когда-либо.
Я не считаю, что это в начальной реализации, но вместо того, чтобы быть реализованным как [внутренние] классы, lambdas может использовать обработчики методов. Производительность ручек метода несколько отстает от предыдущих прогнозов. Уход с классами должен уменьшить охват байт-кода, возможно, время выполнения и, конечно же, время загрузки класса. Может существовать реализация, в которой большинство анонимных внутренних классов (не Serializable
, тривиальный статический инициализатор) не прошли через плохо продуманный механизм загрузки классов без каких-либо особо заметных несовместимостей.
(Надеюсь, у меня есть правильная терминология, верная.)