Это – перевод статьи How to start working with lambda expressions, автора Luis Santiago.
Прежде чем лямбда-выражения появились в Java 8 я успел попробовать их аналоги в других языках, например в C# и C++.
Появление лямбда-выражений расширило синтаксис Java, придав ему дополнительную выразительность. В этой статье я хочу сосредоточиться на основных концепциях, с которыми вам предстоит познакомиться, чтобы начать использовать лямбда-выражения в собственных проектах.
Краткое введение
Из поддержки pipeline-операций в Stream API следует, что лямбда-выражения используют преимущества параллельной обработки в многоядерных системах.
Для реализации методов, определённых в функциональных интерфейсах, используются анонимные методы. Прежде чем приступить к практическим примерам важно понять, что такое функциональный интерфейс.
Функциональные интерфейсы
Функциональный интерфейс – это интерфейс, который содержит один, и только один (единственный!) абстрактный метод.
Если вы взглянете на сигнатуру стандартного интерфейса Runnable
в Java, то наверняка заметите, что он является функциональным, так как определяет единственный метод run()
.
В примере ниже метод computeName
абстрактный (неявно) и единственный, что делает интерфейс MyName
функциональным.
interface MyName{ String computeName(String str); }
Оператор ->
Вместе с лямбда-выражениями в Java был добавлен новый оператор ->
, который делит выражение на две части: (n) -> n * n
.
В левой части указываются параметры выражения, но их может и не быть.
Правая часть – тело выражения, которое задаёт действия. Для большей наглядности можно воспринимать этот оператор как глагол “становится”. Например “n становится n * n” .
Теперь, зная определение функционального интерфейса и оператор ->
, мы можем написать простое лямбда-выражение:
interface NumericTest { boolean computeTest(int n); } public static void main(String args[]) { NumericTest isEven = (n) -> (n % 2) == 0; NumericTest isNegative = (n) -> (n < 0); // Output: false System.out.println(isEven.computeTest(5)); // Output: true System.out.println(isNegative.computeTest(-5)); }
И ещё один пример:
interface MyGreeting { String processName(String str); } public static void main(String args[]) { MyGreeting morningGreeting = (str) -> "Good Morning " + str + "!"; MyGreeting eveningGreeting = (str) -> "Good Evening " + str + "!"; // Output: Good Morning Luis! System.out.println(morningGreeting.processName("Luis")); // Output: Good Evening Jessica! System.out.println(eveningGreeting.processName("Jessica")); }
Переменные morningGreeting
и eveningGreeting
ссылаются на интерфейс MyGreeting
и определяют разные выражения для приветствий.
При написании лямбда-выражений можно явно указывать тип используемого параметра:
MyGreeting morningGreeting = (String str) -> "Good Morning " + str + "!"; MyGreeting eveningGreeting = (String str) -> "Good Evening " + str + "!";
Блочные лямбда-выражения
Пока мы рассматривали только примеры одиночных выражений. Существует ещё один тип, в котором код правой части выражения состоит из нескольких операторов:
interface MyString { String myStringFunction(String str); } class Test { public static void main(String[] args) { // Block lambda to reverse string MyString reverseStr = (str) -> { String result = ""; for(int i = str.length()-1; i >= 0; i--) result += str.charAt(i); return result; }; // Output: omeD adbmaL System.out.println(reverseStr.myStringFunction("Lambda Demo")); } }
Обобщённые функциональные интерфейсы
Лямбда-выражение не может быть обобщённым, но связанный с ним функциональный интерфейс – вполне. Можно создать один обобщённый интерфейс и работать с разными возвращаемыми типами:
interface MyGeneric<T> { T compute(T t); } public static void main(String args[]){ // String version of MyGenericInteface MyGeneric<String> reverse = (str) -> { String result = ""; for(int i = str.length()-1; i >= 0; i--) result += str.charAt(i); return result; }; // Integer version of MyGeneric MyGeneric<Integer> factorial = (Integer n) -> { int result = 1; for(int i=1; i <= n; i++) result = i * result; return result; }; // Output: omeD adbmaL System.out.println(reverse.compute("Lambda Demo")); // Output: 120 System.out.println(factorial.compute(5)); }
Лямбда-выражения в качестве аргументов
Одно из обычных применений лямбда-выражений – это их использование в качестве аргументов.
Они могут использоваться везде, где возвращается конечный тип. Это позволяет передавать функции в качестве аргументов.
Чтобы передать лямбда-выражение в качестве параметра нужно убедиться, что функциональный интерфейс совместим с его типом:
interface MyString { String myStringFunction(String str); } public static String reverseStr(MyString reverse, String str){ return reverse.myStringFunction(str); } public static void main (String args[]) { // Block lambda to reverse string MyString reverse = (str) -> { String result = ""; for(int i = str.length() - 1; i >= 0; i--) result += str.charAt(i); return result; }; // Output: omeD adbmaL System.out.println(reverseStr(reverse, "Lambda Demo")); }
Эти концепции дадут вам неплохую базу для начала работы с лямбда-выражениями.