Библиотека Java разработчика
10.6K subscribers
1.16K photos
583 videos
58 files
1.48K links
📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate.


По всем вопросам @evgenycarter

РКН clck.ru/3KoGeP
Download Telegram
Функциональные интерфейсы: Магия за кулисами Лямбд

Если вы используете лямбды (а вы наверняка их используете), значит, вы работаете с функциональными интерфейсами. Давайте разберем, что это такое, зачем нужна аннотация и какие интерфейсы вы обязаны знать наизусть.

🎯 Что это такое?

Функциональный интерфейс - это интерфейс, который содержит ровно один абстрактный метод.

Именно это ограничение позволяет компилятору превращать лямбда-выражение в экземпляр этого интерфейса.

💙 Примечание: В интерфейсе может быть сколько угодно default или static методов. Главное - только один абстрактный.

📝 Аннотация @FunctionalInterface

Ставить её над интерфейсом не обязательно, но хорошим тоном считается ставить.
Зачем? Она работает как защита от дурака: если вы или ваш коллега случайно добавите второй абстрактный метод в интерфейс, компилятор сразу выдаст ошибку, не дожидаясь падения кода в местах использования лямбд.



🧰 Шпаргалка: "Великолепная четверка"

В пакете java.util.function уже есть готовые интерфейсы на 99% случаев жизни. Не пишите свои велосипеды, пока не выучите эти:

1. Predicate <T>
💙Что делает: Проверяет условие.
💙 Метод: boolean test(T t)
💙 Где нужен: Фильтрация стримов (`filter`), проверки.


2. Consumer <T>
💙 Что делает: "Потребляет" объект, ничего не возвращая.
💙 Метод: void accept(T t)
💙 Где нужен: Вывод на экран, запись в БД, forEach.


3. Supplier <T>
💙 Что делает: "Поставляет" объект (из ниоткуда), ничего не принимая.
💙 Метод: T get()
💙 Где нужен: Ленивая инициализация, генерация значений, orElseGet.


4. Function <T, R>
💙 Что делает: Превращает объект типа T в объект типа R.
💙 Метод: R apply(T t)
💙 Где нужен: Преобразование данных, map в стримах.


💻 Пример в коде


@FunctionalInterface
interface Converter {
int stringToInt(String s);
}

public class Main {
public static void main(String[] args) {
// 1. Создаем реализацию через лямбду
Converter converter = str -> Integer.parseInt(str);

// 2. Использование стандартного Consumer
java.util.function.Consumer<Integer> print = x -> System.out.println("Result: " + x);

int result = converter.stringToInt("123");
print.accept(result); // Вывод: Result: 123
}
}



🔥 Итог

Функциональные интерфейсы - это контракт для лямбда-выражений. Используйте стандартные из java.util.function, чтобы ваш код был понятен другим разработчикам без лишних документаций.

#Java #Core #Lambda #FunctionalProgramming

📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7👍1
🚀 Функциональные интерфейсы: Level Up

В первой части мы разобрали «Великолепную четверку» (Predicate, Consumer, Supplier, Function). Но что делать, если нужно принять два аргумента? Или если тип входа и выхода совпадает, и лень писать лишний код?

Для этого в Java есть Bi-версии и Операторы.

👯 Семейство «Bi» (Два аргумента)

Стандартные интерфейсы принимают только один параметр. Если вам нужно обработать пару значений (например, ключ и значение из Map), используйте приставку Bi.

1. BiPredicate <T, U>
💙 Метод: boolean test(T t, U u)
💙 Пример: Проверить, что длина строки T больше числа U.


2. BiConsumer <T, U>
💙 Метод: void accept(T t, U u)
💙 Пример: map.forEach((k, v) -> ...) - классический пример использования.


3. BiFunction <T, U, R>
💙 Метод: R apply(T t, U u)
💙 Пример: Сложить число T и число U, получить результат R.



🔄 Семейство «Operator» (Один и тот же тип)

Часто бывает, что вы преобразуете объект, не меняя его тип (String -> String, int -> int). Писать Function<String, String> - слишком длинно.

1. UnaryOperator <T>
💙 Наследник Function<T, T>.
💙 Пример: str -> str.toUpperCase() (принимает строку, возвращает строку).


2. BinaryOperator <T>
💙 Наследник BiFunction<T, T, T>.
💙 Пример: (a, b) -> a + b (два числа на вход, одно число на выход). Именно он используется в Stream.reduce.


Осторожно с боксингом!

Дженерики (<T>) работают только с объектами. Если вы используете Function<Integer, Integer> для математики, Java будет постоянно распаковывать и запаковывать int в Integer, что бьет по производительности.

Для примитивов есть свои спецназовцы:

💙 IntPredicate, LongConsumer, DoubleFunction и т.д.
💙 Правило: Если работаете с числами - всегда ищите примитивный аналог интерфейса.

💻 Пример в коде


import java.util.function.*;

public class AdvancedLambdas {
public static void main(String[] args) {
// 1. BinaryOperator: объединяем две строки
BinaryOperator<String> concat = (s1, s2) -> s1 + " " + s2;
System.out.println(concat.apply("Hello", "Java"));

// 2. BiPredicate: проверяем, начинается ли строка с префикса
BiPredicate<String, String> startsWith = (str, prefix) -> str.startsWith(prefix);
System.out.println(startsWith.test("Telegram", "Tele")); // true

// 3. IntUnaryOperator: работаем с примитивами без лишних объектов
IntUnaryOperator square = x -> x * x;
System.out.println(square.applyAsInt(5)); // 25
}
}



🔥 Итог

💙 Нужно 2 аргумента? Ищите Bi.
💙 Тип входа и выхода совпадает? Ищите Operator.
💙 Работаете с int/long/double? Ищите интерфейсы с префиксом типа.

#Java #AdvancedJava #FunctionalProgramming

📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
6