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

Java 8 新特性Новые возможности Java 8


Лямбда-выражения, также известные как замыкания, — важнейшая новая возможность, благодаря которой вышел Java 8.

Лямбда позволяет передать функцию в метод в качестве параметра (функции передаются в методы как аргументы).

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

Синтаксис

Синтаксис лямбда-выражения выглядит следующим образом:

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

parameters— это список параметров,expressionили{ statements; }— это тело лямбда-выражения. Если есть только один параметр, круглые скобки можно опустить; если параметров нет, пустые круглые скобки всё равно обязательны.

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

Пример

// Использование лямбда-выражения для вычисления суммы двух чисел
MathOperation addition = (a, b) -> a + b;

// Вызов лямбда-выражения
int result = addition.operation(5, 3);
System.out.println("5 + 3 = " + result);

В приведённом выше примере MathOperation — это функциональный интерфейс, содержащий абстрактный метод operation. Лямбда-выражение(a, b) -> a + bреализует этот абстрактный метод, представляя операцию сложения двух параметров.

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

Пример

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// Использование лямбда-выражения для перебора списка
names.forEach(name -> System.out.println(name));

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


Важные особенности

Лаконичность

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

Пример

// Традиционный анонимный внутренний класс
Runnable runnable1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello World!");
    }
};

// Использование лямбда-выражения
Runnable runnable2 = () -> System.out.println("Hello World!");

Поддержка функционального программирования

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

Пример

// Передача лямбда-выражения в качестве аргумента методу
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(name -> System.out.println(name));

Захват переменных

Лямбда-выражения могут обращаться к переменным во внешней области видимости. Эта возможность называется захватом переменных. Лямбда-выражения могут неявно захватывать локальные переменные, которые являются final или эффективно final.

Пример

// Захват переменных
int x = 10;
MyFunction myFunction = y -> System.out.println(x + y);
myFunction.doSomething(5); // Вывод 15

Ссылки на методы

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

Пример

// Использование ссылки на метод
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(System.out::println);

Параллелизм

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

Пример

// Использование лямбда-выражений и Stream API для параллельных вычислений
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.parallelStream().mapToInt(Integer::intValue).sum();

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


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

Простые примеры лямбда-выражений:

// 1. 不需要参数,返回值为 5  
() -> 5  
  
// 2. 接收一个参数(数字类型),返回其2倍的值  
x -> 2 * x  
  
// 3. 接受2个参数(数字),并返回他们的差值  
(x, y) -> x – y  
  
// 4. 接收2个int型整数,返回他们的和  
(int x, int y) -> x + y  
  
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
(String s) -> System.out.print(s)

Введите следующий код в файл Java8Tester.java:

Файл Java8Tester.java

public class Java8Tester { public static void main(String args[]){ Java8Tester tester = new Java8Tester(); //Объявление типа MathOperation addition = (int a, int b) -> a + b; //Без объявления типа MathOperation subtraction = (a, b) -> a - b; //Оператор return в фигурных скобках MathOperation multiplication = (int a, int b) -> { return a * b; }; //Без фигурных скобок и без оператора return MathOperation division = (int a, int b) -> a / b; System.out.println("10 + 5 = " + tester.operate(10, 5, addition)); System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction)); System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication)); System.out.println("10 / 5 = " + tester.operate(10, 5, division)); //Без круглых скобок GreetingService greetService1 = message -> System.out.println("Hello " + message); //С круглыми скобками GreetingService greetService2 = (message) -> System.out.println("Hello " + message⟧greetService1); greetService1.sayMessage("Example"); greetService2.sayMessage("Google"); } interface interface { int operation(int a, int b); } interface GreetingService { void sayMessage(String message); } private int operate(int a, int b, MathOperation mathOperation){ return mathOperation.operation(a, b); } }

Выполните приведенный выше скрипт, и вывод будет:

$ javac Java8Tester.java 
$ java Java8Tester
10 + 5 = 15
10 - 5 = 5
10 x 5 = 50
10 / 5 = 2
Hello Example
Hello Google

При использовании лямбда-выражений следует учитывать следующие два момента:

  • Лямбда-выражения в основном используются для определения интерфейсов методного типа для встроенного выполнения (например, простой интерфейс метода). В приведенном выше примере мы использовали различные типы лямбда-выражений для определения методов интерфейса MathOperation, а затем определили выполнение operation.

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


Область видимости переменных

Лямбда-выражения могут ссылаться только на внешние локальные переменные, помеченные как final. То есть локальные переменные, определённые вне области видимости лямбда-выражения, не могут быть изменены внутри лямбда-выражения; в противном случае возникнет ошибка компиляции.

Введите следующий код в файл Java8Tester.java:

Файл Java8Tester.java

public class Java8Tester { final static String salutation = "Hello! "; public static void main(String args[]){ GreetingService greetService1 = message -> System.out.println(salutation + message⟧greetService1); greetService1.sayMessage("Example"); } interface GreetingService { void sayMessage(String message); } }

Выполните приведенный выше скрипт, и вывод будет:

$ javac Java8Tester.java 
$ java Java8Tester
Hello! Example

Мы также можем напрямую обращаться к внешним локальным переменным в лямбда-выражении:

Файл Java8Tester.java

public class Java8Tester { public static void main(String args[]) { final int num = 1; Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num)); s.convert(2); //Вывод равен 3 } public interface Converter<T1, T2> { void convert(int i); } }

Локальные переменные в лямбда-выражениях не нужно объявлять как final, но они не должны изменяться последующим кодом (то есть они неявно имеют семантику final).

int num = 1;  
Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2);
num = 5;  
//报错信息:Local variable num defined in an enclosing scope must be final or effectively 
 final

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

String first = "";  
Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length());  //编译会出错 

Java 8 新特性Новые возможности Java 8

Другие расширения