Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

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

Лямбда в Java всегда имеет тип, и этот тип — функциональный интерфейс. Функциональный интерфейс — это интерфейс ровно с одним абстрактным методом. Лямбда и есть реализация этого единственного метода.

Свой функциональный интерфейс

Любой интерфейс с одним абстрактным методом подходит для лямбды. Аннотация @FunctionalInterface не обязательна, но просит компилятор проверить, что метод действительно один:

@FunctionalInterface
interface Discount {
    double apply(double price);
}
 
void main() {
    Discount half = price -> price / 2;
    Discount minusFive = price -> price - 5;
    IO.println(half.apply(40));
    IO.println(minusFive.apply(40));
}

Результат:

20.0
35.0

Function и Predicate

Писать свой интерфейс приходится редко: в пакете java.util.function есть готовые. Function<T, R> принимает T и возвращает R (метод apply()), а Predicate<T> проверяет условие и возвращает boolean (метод test()):

void main() {
    Function<String, Integer> length = s -> s.length();
    Predicate<Integer> isAdult = age -> age >= 18;
 
    IO.println(length.apply("Анна"));
    IO.println(isAdult.test(15));
    IO.println(isAdult.test(30));
}

Результат:

4
false
true

Consumer и Supplier

Consumer<T> принимает значение и ничего не возвращает — он что-то делает: печатает, сохраняет (метод accept()). Supplier<T>, наоборот, ничего не принимает и выдаёт значение (метод get()):

void main() {
    Consumer<String> greet = name -> IO.println("Привет, " + name + "!");
    Supplier<List<String>> newList = () -> new ArrayList<>();
 
    greet.accept("Олег");
    List<String> list = newList.get();
    IO.println(list.isEmpty());
}

Результат:

Привет, Олег!
true

Другие стандартные интерфейсы

Для двух аргументов есть BiFunction<T, U, R> и BiPredicate<T, U>. Если типы аргумента и результата совпадают, используют UnaryOperator<T> и BinaryOperator<T>:

void main() {
    BiFunction<String, Integer, String> repeat = (s, n) -> s.repeat(n);
    UnaryOperator<String> trim = s -> s.strip();
    BinaryOperator<Integer> max = (a, b) -> a > b ? a : b;
 
    IO.println(repeat.apply("ха", 3));
    IO.println("[" + trim.apply("  текст  ") + "]");
    IO.println(max.apply(7, 4));
}

Результат:

хахаха
[текст]
7

Есть и версии для примитивов, чтобы не упаковывать числа в объекты: IntPredicate, IntFunction, ToIntFunction и другие.

Комбинирование

У стандартных интерфейсов есть методы по умолчанию для сборки сложных функций из простых. Условия соединяют через and(), or() и negate(), а функции выстраивают в цепочку через andThen():

void main() {
    Predicate<String> notEmpty = s -> !s.isEmpty();
    Predicate<String> short5 = s -> s.length() <= 5;
    Predicate<String> goodName = notEmpty.and(short5);
 
    Function<Integer, Integer> doubled = x -> x * 2;
    Function<Integer, String> doubledText = doubled.andThen(x -> "Результат: " + x);
 
    IO.println(goodName.test("Анна"));
    IO.println(goodName.test("Александра"));
    IO.println(doubledText.apply(21));
}

Результат:

true
false
Результат: 42

Частая ошибка

Интерфейс с двумя абстрактными методами не может быть типом лямбды — Java не поймёт, какой из них она реализует. С аннотацией @FunctionalInterface ошибка видна сразу в объявлении:

@FunctionalInterface
interface Calculator {
    int add(int a, int b);
    int subtract(int a, int b);
}
 
void main() {
}

Результат:

Main.java:1: error: Unexpected @FunctionalInterface annotation
@FunctionalInterface
^
  Calculator is not a functional interface
    multiple non-overriding abstract methods found in interface Calculator

Оставьте в интерфейсе один абстрактный метод, а остальные сделайте default — или разделите его на два интерфейса.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.