Lambda ·
‹ Предыдущий Следующий ›
⏱ 5 минут чтения Обновлено: 2026-09-08

Лямбда-выражения в Java 8: основы и примеры. Практические Задания

Самая частая поломка в задаче 2 из этого списка выглядит так: два предиката переставлены местами. Вызов notEmpty.and(notNull).test(null) падает с NullPointerException, хотя проверка на null в коде есть. Причина — and() вычисляет условия слева направо с коротким замыканием, поэтому первым отрабатывает !s.isEmpty() на null. Правильный порядок — notNull.and(notEmpty). Такие детали не видны в теории и всплывают только тогда, когда пишешь код сам.

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

Что повторить перед решением

Каждая задача опирается на один встроенный функциональный интерфейс из пакета java.util.function. Если забыли сигнатуру — откройте соответствующий урок в отдельной вкладке, но решение пишите сами.

Задача Интерфейс Абстрактный метод Что отрабатываем
1, 7 Свой интерфейс Printable void print() Лямбда без параметров, ссылка на метод
2, 3 Predicate<T> boolean test(T t) Композиция: and(), or(), negate()
4 Consumer<T> void accept(T t) Цепочка действий через andThen()
5 Function<T, R> R apply(T t) Преобразование значения одного типа в другой
6 Supplier<T> T get() Поставщик значения без входных параметров

Общая теория — в уроке лямбда-выражения в Java.

Задача 1. Лямбда-выражение для интерфейса Printable

Объявите свой интерфейс Printable с единственным методом void print() и создайте его реализацию в виде лямбда-выражения, которое выводит произвольное сообщение в консоль.

@FunctionalInterface
interface Printable {
    void print();
}
// Printable printable = ...;
// printable.print();

Обратите внимание: у метода нет параметров, поэтому список параметров лямбды — пустые скобки. Аннотация @FunctionalInterface не обязательна, но заставит компилятор проверить, что абстрактный метод действительно ровно один.

Задача 2. Проверка строки на null и пустоту (Predicate.and)

Задание состоит из трёх шагов и выполняется на функциональном интерфейсе Predicate:

  1. Напишите лямбда-выражение Predicate<String>, которое возвращает true, если строка не равна null.
  2. Напишите второе лямбда-выражение Predicate<String>, которое возвращает true, если строка не пустая.
  3. Скомбинируйте оба предиката методом по умолчанию and() в одну проверку «строка не null и не пустая» и протестируйте её на значениях null, "" и "Java".

Порядок важен

Метод and() реализован как t -> test(t) && other.test(t), то есть работает с коротким замыканием слева направо. Предикат, который защищает от null, должен стоять первым, иначе третий пункт задачи будет падать на входном значении null.

Видеорешение задачи →

Задача 3. Первая и последняя буква строки (Predicate.or)

Напишите программу, которая проверяет, что строка начинается с заглавной буквы 'J' или 'N' и при этом заканчивается заглавной буквой 'A'. Проверку соберите из отдельных Predicate<String> с помощью методов or() и and().

Проверьте результат на строках "Java", "Nika", "Scala" и "java". Регистр учитывается: "java" должна вернуть false.

Видеорешение задачи →

Задача 4. Consumer и метод andThen

Есть класс HeavyBox с полем «вес». Напишите лямбда-выражение типа Consumer<HeavyBox>, которое принимает объект HeavyBox и выводит в консоль сообщение «Отгрузили ящик с весом n», где n — вес этого ящика.

Затем создайте второй Consumer<HeavyBox> (например, выводящий адрес доставки) и объедините оба действия в цепочку методом по умолчанию andThen(). Убедитесь, что оба сообщения выводятся для одного и того же объекта и в правильном порядке.

Видеорешение задачи →

Задача 5. Знак числа через Function

Напишите лямбда-выражение типа Function<Integer, String>, которое принимает число и возвращает строку «Положительное число», «Отрицательное число» или «Ноль».

Function<Integer, String> sign = n -> /* ваш код */;

System.out.println(sign.apply(7));   // Положительное число
System.out.println(sign.apply(-3));  // Отрицательное число
System.out.println(sign.apply(0));   // Ноль

Видеорешение задачи →

Задача 6. Случайное число через Supplier

Напишите лямбда-выражение типа Supplier<Integer>, которое возвращает случайное целое число от 0 до 10 включительно. Вызовите get() несколько раз в цикле и убедитесь, что значения меняются, а границы диапазона не нарушаются.

Видеорешение задачи →

Задача 7. Ссылка на статический метод

Вернитесь к задаче 1 и перепишите реализацию Printable через ссылку на статический метод: вынесите тело лямбды в отдельный static-метод и передайте его как ClassName::methodName.

Когда ссылка лучше лямбды

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

Как проверить свои решения

Перед тем как сверяться с видео, пройдитесь по короткому чек-листу — он ловит большую часть типичных промахов новичков:

  1. Скомпилировался ли код без предупреждений и без явного приведения типов? Если требуется каст — скорее всего, выбран не тот функциональный интерфейс.
  2. Проверены ли граничные значения: null, пустая строка, ноль, отрицательное число?
  3. Не изменяется ли внутри лямбды локальная переменная внешнего метода? Такой код не скомпилируется: захватываемые переменные должны быть effectively final.
  4. Нет ли фигурных скобок и return там, где хватило бы однострочного выражения?
  5. Соответствует ли оформление рекомендациям по оформлению кода?

Часто задаваемые вопросы

Что такое лямбда-выражение в Java простыми словами?

Это короткая запись вида параметры -> тело, которая заменяет анонимный класс, реализующий функциональный интерфейс. Компилятор сам понимает, какой метод вы реализуете, потому что у такого интерфейса есть ровно один абстрактный метод. Лямбды доступны начиная с Java 8.

Чем лямбда-выражение отличается от анонимного класса?

Анонимный класс создаёт отдельный класс и собственную область видимости, поэтому this внутри него указывает на сам анонимный объект. В лямбде this ссылается на объемлющий класс, отдельный class-файл не создаётся, а вызов транслируется через invokedynamic. Кроме того, анонимный класс может реализовать интерфейс с любым числом методов, а лямбда — только функциональный.

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

Лямбда захватывает копию значения локальной переменной, а не саму переменную, поэтому Java требует, чтобы переменная была effectively final: ей присваивают значение один раз и больше не меняют. Если счётчик всё же нужен, используйте поле класса, массив из одного элемента или AtomicInteger.

Какой функциональный интерфейс выбрать: Predicate, Function, Consumer или Supplier?

Смотрите на вход и выход. Есть аргумент и нужен boolean — Predicate. Есть аргумент и нужно значение другого типа — Function. Есть аргумент, а результата нет, только действие — Consumer. Аргумента нет, а значение вернуть надо — Supplier. Для примитивов берите специализации вроде IntPredicate или IntSupplier, чтобы избежать лишней упаковки.

Комментарии

unknown Dec 21, 2022
Возможное решение, по требованиям подходит! спойлер!!! System.out.println("Задача №3"); Predicate pr4 = s -> (s.startsWith("J") || s.startsWith("N")) && s.endsWith("A"); System.out.println(pr4.test(test)); System.out.println("--------------");
unknown Dec 21, 2022
System.out.println("Задача №5"); Function fun = x -> { if(x > 0) return "+ число"; if(x < 0) return "- число"; else return "0"; }; System.out.println("--------------");
unknown Dec 21, 2022
System.out.println("Задача №6"); Supplier sup = () -> (int)Math.round(Math.random() * 10); System.out.println(sup.get());

Зарегистрируйтесь или войдите, чтобы иметь возможность оставить комментарий.