Функциональные интерфейсы*
Вам нужно просто прочитать его. Команды выполнять не требуется.
В java определён ряд готовых интерфейсов, которые часто используются при написании кода. Эти интерфейсы содержат всего один метод.
Predicate<T>
Функциональный интерфейс Predicate<T>
проверяет соблюдение некоторого условия.
Если оно соблюдается, то возвращается значение true. В скобках <>
указывается тип данных,
с которым будет работать данный класс. Классы, использующие такие скобки, называются
шаблонами (Generics). Общая идея заключается в том, что если группа классов имеет
одинаковое поведение для разных типов, то нет смысла писать отдельные классы для каждого из них.
Проще сделать шаблон, в который java будет просто подставлять нужный нам тип. Но в шаблонах нельзя
использовать базовые типы, вместо них нужно указывать их классы-оболочки.
public interface Predicate<T> {
boolean test(T t);
}
public static void main(String[] args) {
Predicate<Integer> checkInt = new Predicate<>() {
@Override
public boolean test(Integer value) {
return value > 0;
}
};
Predicate<Double> checkDouble = new Predicate<>() {
@Override
public boolean test(Double value) {
return value > 0.0 && value < 10.0;
}
};
}
Для вызова конструктора класса-шаблона не нужно указывать в скобках тип, достаточно указать его при объявлении переменной.
System.out.println(checkInt.test(5));
System.out.println(checkDouble.test(-1.0));
После этого мы можем в любом месте метода main вызывать проверку чисел при помощи метода test.
Но такая форма записи довольно громоздкая. Чтобы её упростить, используют lambda-выражения.
Общий вид lambda-выражения: (переменные)->{тело метода функционального интерфейса} Набор переменных определяется функциональным интерфейсом, создание которого мы хотим упростить.
public static void main(String[] args) {
Predicate<Integer> checkInt = (value) -> {
return value > 0;
};
Predicate<Double> checkDouble = (value) -> {
return value > 0.0 && value < 10.0;
};
System.out.println(checkInt.test(5));
System.out.println(checkDouble.test(-1.0));
}
Если lambda-выражение не содержит несколько команд, а просто возвращает результат, то можно упростить его ещё сильнее: (переменные)->результат.
public static void main(String[] args) {
Predicate<Integer> checkInt = (value) -> value > 0;
Predicate<Double> checkDouble = (value) -> value > 0.0 && value < 10.0;
System.out.println(checkInt.test(5));
System.out.println(checkDouble.test(-1.0));
}
BinaryOperator<T>
BinaryOperator<T>
принимает в качестве параметра два объекта типа T, выполняет над ними бинарную операцию
и возвращает ее результат также в виде объекта типа T:
public interface BinaryOperator<T> {
T apply(T t1, T t2);
}
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);
}
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);
}
public static void main(String[] args) {
Function<Integer, String> convert = x-> String.valueOf(x) + " долларов";
System.out.println(convert.apply(5)); // 5 долларов
}
Consumer<T>
Consumer<T>
выполняет некоторое действие над объектом типа T
, при этом ничего не возвращая:
public interface Consumer<T> {
void accept(T t);
}
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();
}
При помощи Supplier<T>
часто реализовывают фабрики. Это специальный приём(паттерн проектирования),
который в общем виде создаёт объекты разных классов одним методом. Но все эти классы должны быть
потомками единого предка.
Например, создадим фабрику пользователей:
- Main.java
- User.java
public static void main(String[] args) {
Supplier<User> userFactory = ()->{
Scanner in = new Scanner(System.in);
System.out.println("Введите имя: ");
String name = in.nextLine();
return new User(name);
};
User user1 = userFactory.get();
User user2 = userFactory.get();
System.out.println("Имя user1: " + user1.getName());
System.out.println("Имя user2: " + user2.getName());
}
class User{
private String name;
String getName(){
return name;
}
User(String n){
this.name=n;
}
}
Comparable <T>
Comparable<T>
принимает два аргумента и возвращает -1, если первый объект меньше второго, 0,
если они равны и 1, если больше. Он нужен для использования готовых методов сортировки в коллекциях,
о них мы будем говорить позже
public interface Comparator<T> {
int compare(T a, T b);
}
public static void main(String[] args) {
Comparator<User> personComparator = (p1, p2) -> p1.getName().compareTo(p2.getName());
User person1 = new User("Tom");
User person2 = new User("Nick");
User person3 = new User("Alice");
User person4 = new User("Tom");
System.out.println(personComparator.compare(person1, person2)); // 6
System.out.println(personComparator.compare(person3, person2)); // -13
System.out.println(personComparator.compare(person1, person4)); // 0
}
При реализации компаратора мы использовали готовый метод сравнения строк.