Перейти к основному содержимому

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

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

  • Predicate<T>
  • Consumer<T>
  • Function<T, R>
  • BiFunction<T, U, R>
  • Supplier<T>
  • UnaryOperator<T>
  • BinaryOperator<T>

Predicate<T>​

Функциональный интерфейс Predicate<T> проверяет соблюдение некоторого условия. Если оно соблюдается, то возвращается значение true. В качестве параметра лямбда-выражение принимает объект типа T:

public interface Predicate<T> {
boolean test(T t);
}

Например:

import java.util.function.Predicate;

public class Program {
public static void main(String[] args) {
Predicate<Integer> isPositive = x -> x > 0;

System.out.println(isPositive.test(5)); // true
System.out.println(isPositive.test(-7)); // false
}
}

BinaryOperator<T>​

BinaryOperator<T> принимает в качестве параметра два объекта типа T, выполняет над ними бинарную операцию и возвращает её результат также в виде объекта типа T:

public interface BinaryOperator<T> {
T apply(T t1, T t2);
}

Например:

import java.util.function.BinaryOperator;

public class Program {
public static void main(String[] args) {
BinaryOperator<Integer> multiply = (x, y) -> x * y;

System.out.println(multiply.apply(3, 5)); // 15
System.out.println(multiply.apply(10, -2)); // -20
}
}

UnaryOperator<T>​

UnaryOperator<T> принимает в качестве параметра объект типа T, выполняет над ним операции и возвращает результат операций в виде объекта типа T:

public interface UnaryOperator<T> {
T apply(T t);
}

Например:

import java.util.function.UnaryOperator;

public class Program {
public static void main(String[] args) {
UnaryOperator<Integer> square = x -> x * x;
System.out.println(square.apply(5)); // 25
}
}

Function<T, R>​

Функциональный интерфейс Function<T, R> представляет функцию перехода от объекта типа T к объекту типа R:

public interface Function<T, R> {
R apply(T t);
}

Например:

import java.util.function.Function;

public class Program {
public static void main(String[] args) {
Function<Integer, String> convert = x -> String.valueOf(x) + " долларов";
System.out.println(convert.apply(5)); // 5 долларов
}
}

BiFunction<T, U, R>​

Функциональный интерфейс BiFunction<T, U, R> представляет функцию с двумя параметрами типа T и U и результатом типа R:

public interface BiFunction<T, U, R> {
R apply(T t, U u);
}

Например:

import java.util.function.BiFunction;

public class Program {
public static void main(String[] args) {
BiFunction<Integer, Integer, Integer> sum = (x, y) -> x + y;

System.out.println(sum.apply(3, 5)); // 8
System.out.println(sum.apply(10, -2)); // 8
}
}

Consumer<T>​

Consumer<T> выполняет некоторое действие над объектом типа T, при этом ничего не возвращая:

public interface Consumer<T> {
void accept(T t);
}

Например:

import java.util.function.Consumer;

public class Program {
public static void main(String[] args) {
Consumer<Integer> printer = x -> System.out.printf("%d долларов \n", x);
printer.accept(600); // 600 долларов
}
}

Supplier<T>​

Supplier<T> не принимает никаких аргументов, но должен возвращать объект типа T:

public interface Supplier<T> {
T get();
}

Данный интерфейс используются для так называемых ленивых вычислений, когда результат вычисления может потребоваться лишь спустя некоторое время. Например:

import java.util.function.Supplier;

public class Program {
public static void main(String[] args) {
Supplier<Integer> randomValue = () -> (int)(Math.random() * 10) + 1;

System.out.println("Случайное значение");
System.out.println(randomValue.get()); // реальные вычисления происходят тут
}
}

В данном случае переменная randomValue представляет выражение по вычислению случайного значения, но реальное вычисление происходит, когда вызывается метод get() для получения этого значения.