Лямбда-выражения

Лямбда-выражения появились в Java 8 и позволяют передавать поведение как значение. Раньше для этого приходилось писать анонимные классы — много шаблонного кода ради одного метода. Лямбда — это компактная запись реализации функционального интерфейса (интерфейса с единственным абстрактным методом).

Лямбды особенно полезны при работе с коллекциями, потоками (Stream API), обработчиками событий и любыми API, которым нужно передать «кусочек кода». В контексте AlashEd — это удобный способ описать действие для каждого датчика или для каждого элемента списка измерений.

Синтаксис

(параметры) -> выражение
(параметры) -> { блок_кода; }

// Примеры форм:
() -> 42                       // без параметров
x -> x * 2                     // один параметр, скобки можно опустить
(int x, int y) -> x + y        // с явными типами
(a, b) -> { return a + b; }    // блок с return

Примеры

Пример 1. Замена анонимного класса

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class LambdaVsAnonymous {
    public static void main(String[] args) {
        List<String> sensors = Arrays.asList("ИК", "Ультразвук", "Линия");

        // Старый способ:
        Collections.sort(sensors, new java.util.Comparator<String>() {
            @Override
            public int compare(String a, String b) {
                return a.length() - b.length();
            }
        });
        System.out.println(sensors);

        // Новый способ — лямбда:
        Collections.sort(sensors, (a, b) -> a.length() - b.length());
        System.out.println(sensors);
    }
}

Output:

[ИК, Линия, Ультразвук]
[ИК, Линия, Ультразвук]

Пример 2. forEach по коллекции

import java.util.Arrays;
import java.util.List;

public class LambdaForEach {
    public static void main(String[] args) {
        List<Integer> distances = Arrays.asList(12, 8, 25, 3);
        distances.forEach(d -> System.out.println("Расстояние: " + d + " см"));
    }
}

Output:

Расстояние: 12 см
Расстояние: 8 см
Расстояние: 25 см
Расстояние: 3 см

Пример 3. Лямбда с блоком кода

import java.util.function.BiFunction;

public class LambdaBlock {
    public static void main(String[] args) {
        BiFunction<Integer, Integer, Integer> avg = (a, b) -> {
            int sum = a + b;
            return sum / 2;
        };
        System.out.println("Среднее: " + avg.apply(10, 20));
    }
}

Output:

Среднее: 15

Пример 4. Захват переменных из окружения

import java.util.function.IntUnaryOperator;

public class LambdaCapture {
    public static void main(String[] args) {
        int threshold = 20;  // фактически final
        IntUnaryOperator clamp = v -> v > threshold ? threshold : v;

        System.out.println(clamp.applyAsInt(5));
        System.out.println(clamp.applyAsInt(50));
    }
}

Output:

5
20

Пример 5. Лямбда как обработчик в Runnable

public class LambdaRunnable {
    public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> System.out.println("Поток: " + Thread.currentThread().getName());
        Thread t = new Thread(task);
        t.start();
        t.join();
        System.out.println("Главный поток завершён");
    }
}

Output:

Поток: Thread-0
Главный поток завершён

Подводные камни

Предупреждение

  • Лямбда может захватывать только эффективно финальные локальные переменные. Попытка изменить захваченную переменную внутри лямбды — ошибка компиляции.

  • Внутри лямбды this ссылается на внешний класс, а не на саму лямбду (в отличие от анонимного класса).

  • Лямбда не имеет собственного имени — стек-трейсы у них менее наглядные.

  • Длинные многострочные лямбды трудно читать — лучше вынести в обычный метод и использовать ссылку на метод.

См. также

Примечание

Материал подготовлен по мотивам Oracle Java Tutorials (Lambda Expressions) и распространяется в рамках Oracle Free Documentation License. Текст написан своими словами, примеры кода — оригинальные.