Лямбда-выражения в Java - Вопросы

Всего: 9 вопросов

1. 

Что такое лямбда-выражение в 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.

(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(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 = () -> System.out.println("Hello world");
        function.call();
    }
}
8. 

Может ли лямбда-выражение бросать проверяемое (checked) исключение?

Да, но только если это исключение объявлено в throws абстрактного метода функционального интерфейса. У Consumer.accept() такого объявления нет, поэтому код ниже не скомпилируется:

paths.forEach(p -> Files.readAllBytes(Path.of(p))); // IOException не обработан

Варианты решения: обработать исключение внутри лямбды через try/catch, обернуть его в непроверяемое (например, UncheckedIOException) либо объявить свой функциональный интерфейс с throws.

9. 

Почему локальная переменная, захваченная лямбда-выражением, обязана быть 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());
Страница 1 из 1