Функциональный интерфейс — это интерфейс, определяющий единственный абстрактный метод.
// Функциональный интерфейс, представляющий математическую операцию
@FunctionalInterface
interface MathOperation {
int operate(int a, int b);
}
// Реализации Функционального интерфейса
// Операция сложения
class Addition implements MathOperation {
public int operate(int a, int b) {
return a + b;
}
}
// Операция умножения
class Multiplication implements MathOperation {
public int operate(int a, int b) {
return a * b;
}
}
// Где-то в main-методе
MathOperation addition = new Addition();
MathOperation multiplication = new Multiplication();
System.out.println(addition.operate(5, 3)); // 8
System.out.println(multiplication.operate(5, 3)); // 15Лямбда-выражение — это анонимный метод, реализующий функциональный интерфейс.
Лямбда-оператор -> разделяет лямбда-выражение на две части: в левой части указываются параметры, а в правой — тело лямбда-выражения, реализующее абстрактный метод.
// Функциональный интерфейс
@FunctionalInterface
interface MathOperation {
int operate(int a, int b);
}
// Реализации Функционального интерфейса с помощью лямбда-выражений
MathOperation addition = (a, b) -> a + b;
MathOperation multiplication = (a, b) -> a * b;
System.out.println(addition.operate(5, 3)); // 8
System.out.println(multiplication.operate(5, 3)); // 15Тело одиночного лямбда-выражения состоит из одного выражения.
// Функциональный интерфейс
interface MetricConverter {
double convert(double value);
}
public class MyMainClass {
public static void main(String[] args) {
// Лямбда выражения
MetricConverter feet2meter = (val) -> val / 3.281;
MetricConverter pound2kg = (val) -> val / 2.205;
// Обращения к ссылкам на лямбда выражения
double feet = 1000;
double meter = feet2meter.convert(feet);
System.out.printf("%s футов = %s метров%n", feet, meter);
double pound = 10;
double kg = pound2kg.convert(pound);
System.out.printf("%s фунтов = %s килограмм %n", pound, kg);
}
}Правая часть лямбда-выражения может содержать несколько инструкций заключенных в блок кода.
import java.util.Arrays;
import java.util.List;
// Функциональный интерфейс
interface NumericOperation {
double apply(List<? extends Number> numbers);
}
public class MyMainClass {
public static void main(String[] args) {
// Лямбда выражения
NumericOperation summation = (numbers) -> {
double val = 0;
for (Number n : numbers)
val += n.doubleValue();
return val;
};
// Лямбда выражения
NumericOperation average = (numbers) -> {
if (numbers.size() == 0)
return 0;
double val = summation.apply(numbers);
return val / numbers.size();
};
List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19);
System.out.println("Список: " + Arrays.toString(primes.toArray()));
System.out.println("Сумма = " + summation.apply(primes));
System.out.println("Среднее = " + average.apply(primes));
}
}Функциональный интерфейс Function<T, R> обеспечивает преобразование данных. Метод R apply(T t) принимает один аргумент типа T и возвращает результат типа R.
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}Примеры использования:
// Преобразование строки в ее длину
Function<String, Integer> stringLength = s -> s.length();
System.out.println(stringLength.apply("Hello")); // 5
// Преобразование числа в строку
Function<Integer, String> intToString = n -> "Number: " + n;
System.out.println(intToString.apply(42)); // "Number: 42"
// Цепочка преобразований
Function<String, String> toUpperCase = String::toUpperCase;
Function<String, String> addPrefix = s -> "Mr. " + s;
Function<String, String> combined = toUpperCase.andThen(addPrefix);
System.out.println(combined.apply("john")); // "Mr. JOHN"Функциональный интерфейс Predicate<T> обеспечивает проверку условий. Метод boolean test(T t) принимает один аргумент типа T и проверяет выполнение некоторого условия, накладываемого на аргумент, и возвращает boolean.
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}Примеры использования:
// Проверка строки
Predicate<String> isLong = s -> s.length() > 5;
System.out.println(isLong.test("Hello")); // false
System.out.println(isLong.test("Hello World")); // true
// Проверка на четность
Predicate<Integer> isEven = n -> n % 2 == 0;
System.out.println(isEven.test(4)); // true
System.out.println(isEven.test(5)); // false
// Комбинация предикатов
Predicate<Integer> isPositive = n -> n > 0;
Predicate<Integer> isEvenAndPositive = isEven.and(isPositive);
System.out.println(isEvenAndPositive.test(4)); // true
System.out.println(isEvenAndPositive.test(-2)); // false
System.out.println(isEvenAndPositive.test(3)); // falseФункциональный интерфейс Consumer<T> обеспечивает потребление данных.
Метод void accept(T t) принимает аргумент и не возвращает результат.
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}Примеры использования:
// Простой вывод
Consumer<String> printer = System.out::println;
printer.accept("Hello World!");
// Модификация коллекций
List<String> names = new ArrayList<>();
Consumer<String> addToCollection = names::add;
addToCollection.accept("Alice");
addToCollection.accept("Bob");
// Цепочка потребителей
Consumer<String> print = System.out::println;
Consumer<String> printUpperCase = s -> System.out.println(s.toUpperCase());
Consumer<String> combined = print.andThen(printUpperCase);
combined.accept("hello"); // выведет "hello" и "HELLO"Функциональный интерфейс Supplier<T> обеспечивает поcтавку данных.
Метод T get() не принимает аргументов, возвращает результат типа T.
Примеры использования:
// Поставщик случайных чисел
Supplier<Double> randomSupplier = Math::random;
System.out.println(randomSupplier.get());
// Поставщик текущего времени
Supplier<LocalDateTime> timeSupplier = LocalDateTime::now;
System.out.println(timeSupplier.get());
// Поставщик новых объектов
Supplier<List<String>> listSupplier = ArrayList::new;
List<String> newList = listSupplier.get();
// Ленивая инициализация
Supplier<String> expensiveOperation = () -> {
// Имитация дорогой операции
try { Thread.sleep(1000); } catch (InterruptedException e) {}
return "Result";
};Функциональный интерфейс UnaryOperator<T> обеспечивает преобразование данных , где тип аргумента и возвращаемого значения одинаков.
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
// Наследует T apply(T t)
}Примеры использования:
// Инкремент
UnaryOperator<Integer> increment = x -> x + 1;
System.out.println(increment.apply(5)); // 6
// Работа со строками
UnaryOperator<String> capitalize = s ->
s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
System.out.println(capitalize.apply("hELLO")); // "Hello"
// Композиция операторов
UnaryOperator<Integer> square = x -> x * x;
UnaryOperator<Integer> triple = x -> x * 3;
UnaryOperator<Integer> squaredThenTripled = square.andThen(triple);
System.out.println(squaredThenTripled.apply(4)); // 48 (4²=16, 16×3=48)Функциональный интерфейс BiFunction<T, U, R> обеспечивает реализацию функции двух переменных. Метод R apply(T t, U u) Принимает два аргумента разных типов T и U и возвращает результат третьего типа R.
// Конкатенация строки и числа
BiFunction<String, Integer, String> repeatString = (s, n) -> {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) {
sb.append(s);
}
return sb.toString();
};
System.out.println(repeatString.apply("Hi", 3)); // "HiHiHi"
// Создание объектов
BiFunction<String, Integer, Person> createPerson = Person::new;
Person person = createPerson.apply("Alice", 25);Функциональный интерфейс BinaryOperator<T> обеспечивает реализацию функции двух переменных, совпадающих с результатом по типу данных. Принимает два аргумента одного типа T и возвращает результат того же типа T.
@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T, T, T> {
// Наследает T apply(T t1, T t2)
}Примеры использования:
// Математические операции
BinaryOperator<Integer> sum = Integer::sum;
BinaryOperator<Integer> multiply = (a, b) -> a * b;
System.out.println(sum.apply(5, 3)); // 8
System.out.println(multiply.apply(5, 3)); // 15
// Минимум/максимум
BinaryOperator<Integer> min = Math::min;
BinaryOperator<Integer> max = Math::max;
System.out.println(min.apply(5, 3)); // 3
System.out.println(max.apply(5, 3)); // 5Ссылки на методы — это краткий синтаксис лямбда-выражений, который позволяет ссылаться на существующие методы или конструкторы.
Синтаксис:
ClassName::staticMethodNameПримеры использования:
// Эквивалентные записи:
Function<String, Integer> parser1 = s -> Integer.parseInt(s);
Function<String, Integer> parser2 = Integer::parseInt;
System.out.println(parser2.apply("123")); // 123
// Другие примеры:
Function<Double, Double> squareRoot = Math::sqrt;
System.out.println(squareRoot.apply(16.0)); // 4.0
BiFunction<Integer, Integer, Integer> max = Math::max;
System.out.println(max.apply(5, 10)); // 10
// Использование в Streams
List<String> numbers = Arrays.asList("1", "2", "3");
List<Integer> intNumbers = numbers.stream()
.map(Integer::parseInt) // Статический метод
.collect(Collectors.toList());Синтаксис:
object::instanceMethodNameПримеры использования:
// Создаем объект
String prefix = "Hello, ";
// Эквивалентные записи:
Function<String, String> greeter1 = name -> prefix.concat(name);
Function<String, String> greeter2 = prefix::concat;
System.out.println(greeter2.apply("Alice")); // "Hello, Alice"
// Другой пример:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<String> upperCaseNames = new ArrayList<>();
// Эквивалентные записи:
Consumer<String> addToCollection1 = s -> upperCaseNames.add(s.toUpperCase());
Consumer<String> addToCollection2 = upperCaseNames::add;
names.forEach(upperCaseNames::add);
System.out.println(upperCaseNames); // [Alice, Bob, Charlie]Синтаксис:
ClassName::instanceMethodNameПримеры использования:
// Эквивалентные записи:
Function<String, String> toUpper1 = s -> s.toUpperCase();
Function<String, String> toUpper2 = String::toUpperCase;
System.out.println(toUpper2.apply("hello")); // "HELLO"
// Другой пример:
BiFunction<String, String, Boolean> equals1 = (s1, s2) -> s1.equals(s2);
BiFunction<String, String, Boolean> equals2 = String::equals;
System.out.println(equals2.apply("hello", "hello")); // true
System.out.println(equals2.apply("hello", "world")); // false
// Использование в Streams
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<String> upperNames = names.stream()
.map(String::toUpperCase) // Метод произвольного объекта
.collect(Collectors.toList());Синтаксис:
ClassName::newПримеры использования:
// Конструктор без параметров
Supplier<List<String>> listSupplier1 = () -> new ArrayList<>();
Supplier<List<String>> listSupplier2 = ArrayList::new;
List<String> list = listSupplier2.get();
list.add("Hello");
System.out.println(list); // [Hello]
// Конструктор с параметрами
Function<String, Integer> integerCreator1 = s -> new Integer(s);
Function<String, Integer> integerCreator2 = Integer::new;
System.out.println(integerCreator2.apply("123")); // 123
// Конструктор с двумя параметрами
BiFunction<String, Integer, Person> personCreator1 = (name, age) -> new Person(name, age);
BiFunction<String, Integer, Person> personCreator2 = Person::new;
Person person = personCreator2.apply("Alice", 25);
System.out.println(person.getName()); // AliceКомпаратор можно создать на основе анонимного класса.
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
class Student {
String name;
int age;
float score;
Student(String name, int age, float score) {
this.name = name;
this.age = age;
this.score = score;
}
}
public class MyMainClass {
public static void main(String[] args) {
// Создать список студентов
List<Student> students = Arrays.asList(
new Student("Иван", 21, 3.5f),
new Student("Петя", 22, 4.1f),
new Student("Мария", 20, 4.5f),
new Student("Даша", 20, 3.0f)
);
// Создать компаратор студентов по среднему баллу на основе аннонимного класса
Comparator<Student> scoreComparator = new Comparator<>() {
@Override
public int compare(Student s1, Student s2) {
return Float.compare(s1.score, s2.score);
}
};
// Упорядочить студентов с помощью компаратора
students.sort(scoreComparator);
// Напечатать студентов
students.forEach(
student -> System.out.printf("%s (%s)%n", student.name, student.score)
);
}
}Альтернативно, компаратор можно создать с помощью лямбда-выражения.
// Создать компаратор студентов по среднему баллу с помощью лямбда-выражения
Comparator<Student> scoreComparator =
(Student s1, Student s2) -> Float.compare(s1.score, s2.score);Лямбда-выражения можно использовать для фильтрации объектов из коллекций.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
class Student {
String name;
int age;
float score;
Student(String name, int age, float score) {
this.name = name;
this.age = age;
this.score = score;
}
}
public class MyMainClass {
public static void main(String[] args) {
List<Student> students = Arrays.asList(
new Student("Иван", 21, 3.5f),
new Student("Петя", 22, 4.1f),
new Student("Мария", 20, 4.5f),
new Student("Даша", 20, 3.0f)
);
// Выбрать студентов со средним баллом больше 4
Stream<Student> filtered = students.stream().filter(p -> p.score > 4.0f);
// Напечатать выбранных студентов
filtered.forEach(
student -> System.out.println(student.name)
);
}
}10-1 — 1 балл
- Разработать функциональный интерфейс для выполнения операций над коллекцией чисел.
- Разработать лямбда выражения, реализующие данный функциональный интерфейс, для поиска минимального и максимального значения.
- Применить эти лямбда выражения.
10-2 — 1 балл
- Разработать сущностный класс (например,
Rectangle,Person,Location). - Создать два разных компаратора на основе лямбда-выражений для сортировки коллекции объектов данного класса по двум разным критериям.
- Реализовать фильтрацию коллекции объектов данного класса по заданному критерию.
10-3 — 2 балла
- Разработать систему для фильтрации, сортировки и обработки книг в библиотеке с использованием стандартных функциональных интерфейсов.
- Разработать базовые классы
Book(книга) иLibrary(библиотека) - Объекты класса
Bookдолжны характеризоваться названия, автора, года издания, жанра, рейтинга, количества страниц и доступности. - Объект класса
Libraryдолжен иметь список книг. - Реализовать фильтрацию книг с помощью функционального интерфейса
Predicateпо автору, по году издания, по жанру, по комбинации автора и года издания, и другим условия. - Реализовать преобразование книг с помощью функционального интерфейса
Functionдля следующих случаев: извлечение списка названий книг; извлечение описаний книг в формате "Автор: Название (Год)"; группирование книг по авторам. - Реализовать сортировку с помощью функционального интерфейса
Comparatorпо следующим полям: по названию (лексикографически); по году (по убыванию); сначала по автору; затем по году; по произвольному компаратору. - Реализовать обработку книг с помощью функционального интерфейса
Consumer: увеличить рейтинг всех книг на 0.1; изменить жанр всех книг на заданный. - Реализовать поиск книги с помощью функционального интерфейса
Supplier. - Использовать только стандартные функциональные интерфейсы:
Predicate<T>,Function<T, R>,Consumer<T>,Supplier<T>,Comparator<T>. - Применить различные способы создания лямбда-выражений: явные лямбда-выражения; ссылки на методы; ссылки на конструкторы.
- Использовать методы по умолчанию:
Predicate.and(),Predicate.or(),Comparator.thenComparing()и др.
- Есть ли в вашем коде функциональные интерфейсы?
- Есть ли в вашем коде одиночные лямбда-выражения?
- Есть ли в вашем коде блочные лямбда-выражения?
- Выделите в вашем коде лямбда оператор?
- Выделите в вашем коде параметры лямбда-выражения?
- Выделите в вашем коде тело лямбда-выражения?
- Какой функциональный интерфейс реализует то или иное лямбда-выражения в вашем коде?
- Какие обобщенные функциональные интерфейсы вы применяете в вашем коде?
- Есть ли ссылки на статические методы в вашем коде?
- Есть ли ссылки на методы конкретных объектов в вашем коде?
- Есть ли ссылки на методы произвольных объектов в вашем коде?
- Есть ли ссылки на конструкторы в вашем коде?
- Как вы реализовали компараторы?
- Какие встроенные функциональные интерфейсы вы используете в вашем коде?
- Сколько абстрактных методов может включать функциональный интерфейс?
- Сколько методов по умолчанию может включать функциональный интерфейс?
- Какими способами можно реализовать функциональный интерфейс?
- Какой тип данных имеет то или иное лямбда-выражение в вашем коде?
- Что содержит левая часть лямбда-выражения?
- Что содержит правая часть лямбда-выражения?