Лямбда-выражения в Java - Вопросы
Всего: 9 вопросов
1. Что такое лямбда-выражение в Java?
Что такое лямбда-выражение в Java?
Лямбда-выражение — это компактная запись анонимной функции: метода без имени и без объявления в классе, который можно передать в другой метод как аргумент или сохранить в переменную. Лямбда-выражения появились в Java 8 и позволяют передавать в код само поведение, а не объект, который его содержит.
2. Каков синтаксис лямбда-выражения?
Каков синтаксис лямбда-выражения?
Лямбда-выражение состоит из трёх частей: список параметров, стрелка и тело.
(параметры) -> тело
Например:
(Object arg1, Object arg2) -> arg1.equals(arg2)
3. Нужно ли заключать параметры лямбда-выражения в круглые скобки?
Нужно ли заключать параметры лямбда-выражения в круглые скобки?
Если параметров нет или их больше одного, круглые скобки обязательны.
() -> 42
(a1, a2) -> a1 + a2
(int a1, int a2) -> a1 + a2
4. Нужно ли заключать единственный параметр лямбда-выражения в круглые скобки?
Нужно ли заключать единственный параметр лямбда-выражения в круглые скобки?
Можно, но не обязательно: у единственного параметра скобки разрешено опустить. Однако без скобок нельзя указать тип параметра явно.
a1 -> 2 * a1 // корректно
(a1) -> 2 * a1 // тоже корректно
(int a1) -> 2 * a1 // корректно, но скобки обязательны
int a1 -> 2 * a1 // ошибка компиляции
5. Когда в теле лямбда-выражения можно опустить фигурные скобки и ключевое слово return?
Когда в теле лямбда-выражения можно опустить фигурные скобки и ключевое слово return?
Когда тело состоит из одного выражения — тогда его значение возвращается автоматически. Если инструкций несколько, фигурные скобки обязательны, а для возврата значения нужен явный return.
(int a) -> a * 6
(int a) -> {
System.out.println(a);
return a * 6;
}
6. Перепишите анонимный класс с помощью лямбда-выражения:
JButton button = ...
JLabel comp = ...
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
comp.setText("Button was clicked.");
}
});
Перепишите анонимный класс с помощью лямбда-выражения:
JButton button = ...
JLabel comp = ...
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
comp.setText("Button was clicked.");
}
});JButton button = ...
JLabel comp = ...
button.addActionListener(e -> comp.setText("Button was clicked."));
Интерфейс ActionListener функциональный — у него ровно один абстрактный метод, поэтому компилятор сам выводит тип параметра e из целевого типа.
7. Перепишите код с использованием лямбда-выражения:
interface Function {
void call();
}
class AnonymousInnerClass {
public static void main(String[] args) {
Function function = new Function() {
public void call() {
System.out.println("Hello world");
}
};
function.call();
}
}
Перепишите код с использованием лямбда-выражения:
interface Function {
void call();
}
class AnonymousInnerClass {
public static void main(String[] args) {
Function function = new Function() {
public void call() {
System.out.println("Hello world");
}
};
function.call();
}
}interface Function {
void call();
}
class AnonymousInnerClass {
public static void main(String[] args) {
Function function = () -> System.out.println("Hello world");
function.call();
}
}
8. Может ли лямбда-выражение бросать проверяемое (checked) исключение?
Может ли лямбда-выражение бросать проверяемое (checked) исключение?
Да, но только если это исключение объявлено в throws абстрактного метода функционального интерфейса. У Consumer.accept() такого объявления нет, поэтому код ниже не скомпилируется:
paths.forEach(p -> Files.readAllBytes(Path.of(p))); // IOException не обработан
Варианты решения: обработать исключение внутри лямбды через try/catch, обернуть его в непроверяемое (например, UncheckedIOException) либо объявить свой функциональный интерфейс с throws.
9. Почему локальная переменная, захваченная лямбда-выражением, обязана быть final или effectively final, а поле класса — нет?
Почему локальная переменная, захваченная лямбда-выражением, обязана быть final или effectively final, а поле класса — нет?
Лямбда захватывает значение локальной переменной, а не саму переменную. Локальные переменные живут в стеке метода и исчезают вместе с ним, поэтому лямбда получает их копию — изменять копию бессмысленно и опасно. Отсюда требование: переменная должна быть final или effectively final, то есть фактически не изменяться после инициализации.
int count = 0;
cars.forEach(car -> count++); // error: local variables referenced from
// a lambda expression must be final or effectively final
Поля класса доступны через ссылку на объект в куче, которая остаётся живой столько, сколько живёт лямбда, поэтому их менять можно. Если нужен счётчик, используйте AtomicInteger — сама ссылка не меняется, меняется содержимое объекта:
AtomicInteger counter = new AtomicInteger();
cars.forEach(car -> counter.incrementAndGet());