Java for Beginner
675 subscribers
559 photos
156 videos
12 files
856 links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Продолжаем серию постов для углубления недостаточно рассмотренных и актуальных тем.

Функциональное программирование

Функциональное программирование (ФП) — это парадигма программирования, в которой основным инструментом являются функции. В отличие от императивного программирования, где акцент делается на изменении состояния программы, ФП фокусируется на вычислениях через применение функций.

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


Основные принципы функционального программирования

Неизменяемость (Immutability):
В ФП данные не изменяются после создания. Вместо этого создаются новые данные на основе существующих.

Преимущества:
Упрощение отладки и тестирования.
Потокобезопасность (нет состояния, которое может быть изменено несколькими потоками).


Недостатки:
Может потреблять больше памяти, так как создаются новые объекты вместо изменения существующих.

Пример:
// Императивный стиль (изменяемый)
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Kotlin");

// Функциональный стиль (неизменяемый)
List<String> immutableList = List.of("Java", "Kotlin");


Чистые функции (Pure Functions):

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

Преимущества:
Предсказуемость и простота тестирования.
Упрощение параллельного выполнения.


Недостатки:
Иногда сложно избежать побочных эффектов в реальных приложениях.

Пример:
// Чистая функция
public static int add(int a, int b) {
return a + b;
}

// Нечистая функция (имеет побочный эффект)
public static int addAndLog(int a, int b) {
System.out.println("Adding " + a + " and " + b); // Побочный эффект
return a + b;
}


Функции высшего порядка (Higher-Order Functions):

Это функции, которые могут принимать другие функции в качестве аргументов или возвращать их.

Преимущества:
Гибкость и возможность создания абстракций.

Недостатки:
Может быть сложнее для понимания новичкам.

Пример:
// Функция высшего порядка
public static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
List<T> result = new ArrayList<>();
for (T item : list) {
if (predicate.test(item)) {
result.add(item);
}
}
return result;
}

// Использование
List<Integer> numbers = List.of(1, 2, 3, 4, 5);
List<Integer> evenNumbers = filter(numbers, n -> n % 2 == 0); // [2, 4]


Полезная для понимания вопроса, статья - https://habr.com/ru/articles/570642/

#Java #Training #Medium #Functional_programming
Поддержка функционального программирования в Java

Начиная с Java 8, язык получил мощные инструменты для поддержки функционального программирования:

Лямбда-выражения: Позволяют писать анонимные функции в компактной форме.
Функциональные интерфейсы: Интерфейсы с одним абстрактным методом (SAM), которые могут быть реализованы с помощью лямбда-выражений.
Stream API: Позволяет работать с коллекциями в функциональном стиле.


Лямбда-выражения

Лямбда-выражения — это краткий способ написания анонимных функций.

Они состоят из:
Списка параметров.
Стрелки (->).
Тела функции.


Синтаксис:
(параметры) -> { тело функции }


Примеры:

Простое лямбда-выражение:
// Без лямбды
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("Hello, World!");
}
};

// С лямбдой
Runnable runnable = () -> System.out.println("Hello, World!");


Лямбда с параметрами:
// Без лямбды
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer a, Integer b) {
return a.compareTo(b);
}
};

// С лямбдой
Comparator<Integer> comparator = (a, b) -> a.compareTo(b);


Лямбда с несколькими строками:
// Без лямбды
Function<String, Integer> lengthFunction = new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return s.length();
}
};

// С лямбдой
Function<String, Integer> lengthFunction = s -> {
System.out.println("Calculating length of: " + s);
return s.length();
};


Преимущества лямбда-выражений
Упрощение кода.
Улучшение читаемости.
Поддержка функционального стиля программирования.


Недостатки лямбда-выражений
Могут быть сложны для понимания новичкам.
Отладка лямбда-выражений может быть менее удобной.


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

Функциональные интерфейсы — это интерфейсы, которые содержат ровно один абстрактный метод (Single Abstract Method, SAM). Они используются для представления лямбда-выражений и ссылок на методы.

Примеры функциональных интерфейсов в Java:
Predicate<T>: Принимает один аргумент и возвращает boolean.
Function<T, R>: Принимает один аргумент и возвращает результат.
Consumer<T>: Принимает один аргумент и не возвращает результат.
Supplier<T>: Не принимает аргументов, но возвращает результат.


Пример использования:
// Predicate
Predicate<Integer> isEven = n -> n % 2 == 0;
System.out.println(isEven.test(4)); // true

// Function
Function<String, Integer> lengthFunction = String::length;
System.out.println(lengthFunction.apply("Hello")); // 5

// Consumer
Consumer<String> printConsumer = System.out::println;
printConsumer.accept("Hello, World!");

// Supplier
Supplier<Double> randomSupplier = Math::random;
System.out.println(randomSupplier.get()); // Случайное число


#Java #Training #Medium #Functional_programming #Stream_API #Lambda_expressions #Functional_interfaces
Stream API

Stream API — это мощный инструмент для работы с коллекциями в функциональном стиле. Он позволяет выполнять операции над данными, такие как фильтрация, преобразование, сортировка и агрегация, без изменения исходных данных.


Основные операции Stream API:
Промежуточные операции (intermediate): Возвращают новый поток и могут быть объединены в цепочку.
filter(Predicate<T>): Фильтрует элементы.
map(Function<T, R>): Преобразует элементы.
sorted(): Сортирует элементы.
Терминальные операции (terminal): Завершают поток и возвращают результат.
forEach(Consumer<T>): Выполняет действие для каждого элемента.
collect(Collector<T, A, R>): Собирает элементы в коллекцию.
reduce(BinaryOperator<T>): Сворачивает элементы в одно значение.


Пример использования:
List<String> languages = List.of("Java", "Kotlin", "Scala", "Groovy");

// Фильтрация и преобразование
List<String> filteredLanguages = languages.stream()
.filter(lang -> lang.startsWith("J")) // Фильтруем по условию
.map(String::toUpperCase) // Преобразуем в верхний регистр
.collect(Collectors.toList()); // Собираем в список

System.out.println(filteredLanguages); // [JAVA]

// Агрегация
int totalLength = languages.stream()
.mapToInt(String::length) // Преобразуем в длины строк
.sum(); // Суммируем длины

System.out.println(totalLength); // 18



Преимущества Stream API

Упрощение работы с коллекциями.
Поддержка параллельного выполнения (через parallelStream()).
Читаемость и выразительность кода.


Недостатки Stream API
Может быть менее производительным для простых операций из-за накладных расходов.
Сложность отладки из-за цепочек вызовов.


#Java #Training #Medium #Functional_programming #Stream_API #Lambda_expressions #Functional_interfaces