Это – перевод статьи 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"));
}
Эти концепции дадут вам неплохую базу для начала работы с лямбда-выражениями.