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

Последнее обновление: 02.10.2025

Лямбда-выражения в языке Java представляют набор инструкций, которые можно выделить в отдельную переменную и затем многократно вызвать в различных местах программы.

Определение лямбда-выражения и функциональные интерфейсы

Для создания лямбда-выражения применяется лямбда-оператор (стрелка ->). Этот оператор разделяет лямбда-выражение на две части:

 (параметры) -> действия  

левая часть (до стрелки) содержит параметры через запятую, а правая часть (после стрелки) собственно представляет выполняемые действия лямбда-выражения.

Лямбда-выражение не выполняется само по себе, а образует реализацию метода, определенного в функциональном интерфейсе. Рассмотрим пример:

public class Program {

    public static void main(String[] args) {
        
        Operationable op;
        op = (x,y)->x+y;
        
        int result = op.execute(10, 20);
        System.out.println(result); //30
    }   
}
interface Operationable{
    int execute(int x, int y);
}

Здесь в роли функционального интерфейса выступает интерфейс Operationable, в котором определен один метод без реализации - метод execute. Данный метод принимает два параметра - целых числа, и возвращает некоторое целое число.

Чтобы объявить и использовать лямбда-выражение, основная программа разбивается на ряд этапов:

  1. Определение ссылки на функциональный интерфейс:

    Operationable op;
  2. Создание лямбда-выражения:

    op = (x,y)->x+y;

    Причем параметры лямбда-выражения соответствуют параметрам единственного метода интерфейса Operationable, а результат соответствует возвращаемому результату метода интерфейса. При этом нам не надо использовать ключевое слово return для возврата результата из лямбда-выражения.

    Так, в методе интерфейса оба параметра представляют тип int, значит, в теле лямбда-выражения мы можем применить к ним сложение. Результат сложения также представляет тип int, объект которого возвращается методом интерфейса.

  3. Использование лямбда-выражения в виде вызова метода интерфейса:

    int result = operation.execute(10, 20);

    Так как в лямбда-выражении определена операция сложения параметров, результатом метода будет сумма чисел 10 и 20.

По факту лямбда-выражения являются в некотором роде сокращенной формой внутренних анонимных классов, которые ранее применялись в Java. В частности, предыдущий пример мы можем переписать следующим образом:

public class Program {

    public static void main(String[] args) {
        
        Operationable op = new Operationable(){
        
            public int execute(int x, int y){
            
				return x + y;
            }
        };
         int z = op.execute(20, 10);
         System.out.println(z); // 30
    }   
}
interface Operationable{
    int execute(int x, int y);
}

При этом для одного функционального интерфейса мы можем определить множество лямбда-выражений. Например:

Operationable sum = (int x, int y)-> x + y;
Operationable sub = (int x, int y)-> x - y;
Operationable mul = (int x, int y)-> x * y;
        
System.out.println(sum.execute(20, 10)); //30
System.out.println(sub.execute(20, 10)); //10
System.out.println(mul.execute(20, 10)); //200

Если лямбда-выражение принимает один параметр, то скобки вокруг параметров можно не использовать:

public class Program {

    public static void main(String[] args) {
        
        Printable printer = message -> System.out.println(message);

        printer.print("Hello World");
        printer.print("Hello Work");
    }   
}
interface Printable{
    void print(String message);
}

Здесь лямбда-выражение printer представляет функциональный интерфейс Printable, единственный метод которого - print принимает только один параметр.

Другая ситуация - в методе функционального интерфейса есть параметр, но в лямбда-выражении он не используется. В этом случае вместо параметра можно указать прочерк:

public class Program {

    public static void main(String[] args) {
        
        Printable printer = _ -> System.out.println("Test");

        // при вызове параметру все равно надо передать значение
        // но оно не имеет значения, так как не будет использоваться
        printer.print(null);    // Test
        printer.print("brr");   // Test
    }   
}
interface Printable{
    void print(String message);
}

Если метод интерфейса вообще не принимает никаких параметров, то при определении лямбда-выражения указываются пустые скобки:

public class Program {

    public static void main(String[] args) {
        
        Hello speaker = () -> System.out.println("Привед");

        speaker.sayHello();
    }   
}
interface Hello{
    void sayHello();
}

Отложенное выполнение

Одним из ключевых моментов в использовании лямбд является отложенное выполнение (deferred execution). То есть мы определяем в одном месте программы лямбда-выражение и затем можем его вызывать при необходимости неопределенное количество раз в различных частях программы. Отложенное выполнение может потребоваться, к примеру, в следующих случаях:

  • Выполнение кода отдельном потоке

  • Выполнение одного и того же кода несколько раз

  • Выполнение кода в результате какого-то события

  • Выполнение кода только в том случае, когда он действительно необходим и если он необходим

Лямбды и локальные переменные

Лямбда-выражение может использовать переменные, которые объявлены во вне в более общей области видимости - на уровне класса или метода, в котором лямбда-выражение определено. Однако в зависимости от того, как и где определены переменные, могут различаться способы их использования в лямбдах. Рассмотрим первый пример - использования переменных уровня класса:

public class Program {

    static int x = 10;
    static int y = 20;
    public static void main(String[] args) {
        
        Operation op = ()->{ 
            
            x=30;
            return x+y;
        };
        System.out.println(op.execute()); // 50
        System.out.println(x); // 30 - значение x изменилось
    }
}
interface Operation{
    int execute();
}

Переменные x и y объявлены на уровне класса, и в лямбда-выражении мы их можем получить и даже изменить. Так, в данном случае после выполнения выражения изменяется значение переменной x.

Теперь рассмотрим другой пример - локальные переменные на уровне метода:

public static void main(String[] args) {
        
    int n = 70;
    int m = 30;
    Operation op = ()->{ 
            
        //n = 100; - так нельзя сделать
        return m+n;
    };
	// n=100;  - так тоже нельзя
    System.out.println(op.execute()); // 100
} 

Локальные переменные уровня метода мы также можем использовать в лямбдах, но изменять их значение нельзя. Если мы попробуем это сделать, то среда разработки (Netbeans) может нам высветить ошибку и то, что такую переменную надо пометить с помощью ключевого слова final, то есть сделать константой: final int n=70;. Однако это необязательно.

Более того, мы не сможем изменить значение переменной, которая используется в лямбда-выражении, вне этого выражения. То есть даже если такая переменная не объявлена как константа, по сути она является константой.

Блоки кода в лямбда-выражениях

Существуют два типа лямбда-выражений: однострочное выражение и блок кода. Примеры однострочных выражений демонстрировались выше. Блочные выражения обрамляются фигурными скобками. В блочных лямбда-выражениях можно использовать внутренние вложенные блоки, циклы, конструкции if, switch, создавать переменные и т.д. Если блочное лямбда-выражение должно возвращать значение, то явным образом применяется оператор return:

Operationable operation = (int x, int y)-> {
        
    if(y==0)
        return 0;
    return x/y;
};
        
System.out.println(operation.execute(20, 10)); //2
System.out.println(operation.execute(20, 0)); //0

Обобщенный функциональный интерфейс

Функциональный интерфейс может быть обобщенным, однако в лямбда-выражении использование обобщений не допускается. В этом случае нам надо типизировать объект интерфейса определенным типом, который потом будет применяться в лямбда-выражении. Например:

public class Program {

    public static void main(String[] args) {
        
        Operationable<Integer> operation1 = (x, y)-> x + y;
        Operationable<String> operation2 = (x, y) -> x + y;
        
        System.out.println(operation1.execute(20, 10)); //30
        System.out.println(operation2.execute("20", "10")); //2010
    } 
}
interface Operationable<T>{
    T execute(T x, T y);
}

Таким образом, при объявлении лямбда-выражения ему уже известно, какой тип параметры будут представлять и какой тип они будут возвращать.

Помощь сайту
Юмани:
410011174743222
Номер карты:
4048415020898850
Morty Proxy This is a proxified and sanitized view of the page, visit original site.