← Назад к вопросам
Что такое чистая функция?
2.0 Middle🔥 181 комментариев
#Stream API и функциональное программирование
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Чистая функция
Чистая функция — это функция, которая для одного и того же набора входных параметров всегда возвращает одно и то же значение и не имеет видимых побочных эффектов. Это фундаментальная концепция функционального программирования, которая делает код предсказуемым, тестируемым и параллелизируемым.
Определение чистой функции
Чистая функция должна соответствовать двум критериям:
- Детерминированность — для одного и того же входа функция всегда возвращает один и тот же выход
- Отсутствие побочных эффектов — функция не изменяет внешнее состояние и не имеет наблюдаемых эффектов кроме возврата значения
Примеры чистых функций
public class PureFunctions {
// ЧИСТАЯ функция: математическая операция
public static int add(int a, int b) {
return a + b;
}
// ЧИСТАЯ функция: проверка условия
public static boolean isEven(int number) {
return number % 2 == 0;
}
// ЧИСТАЯ функция: преобразование строки
public static String toUpperCase(String text) {
return text.toUpperCase();
}
// ЧИСТАЯ функция: факториал
public static int factorial(int n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
// ЧИСТАЯ функция: средн. арифметическое
public static double average(int[] numbers) {
int sum = 0;
for (int num : numbers) {
sum += num;
}
return numbers.length > 0 ? (double) sum / numbers.length : 0;
}
public static void main(String[] args) {
// Вызовы чистой функции всегда дают одинаковый результат
System.out.println(add(3, 5)); // 8
System.out.println(add(3, 5)); // 8
System.out.println(add(3, 5)); // 8
System.out.println(isEven(4)); // true
System.out.println(isEven(4)); // true
System.out.println(factorial(5)); // 120
System.out.println(factorial(5)); // 120
}
}
Примеры нечистых функций
public class ImpureFunctions {
private static int counter = 0;
// НЕЧИСТАЯ: зависит от внешнего состояния
public static int incrementCounter() {
return ++counter; // Зависит от переменной counter
}
// НЕЧИСТАЯ: побочный эффект (печать)
public static void printAdd(int a, int b) {
System.out.println(a + b); // Побочный эффект
}
// НЕЧИСТАЯ: использует текущее время
public static long getCurrentTime() {
return System.currentTimeMillis(); // Разные результаты при каждом вызове
}
// НЕЧИСТАЯ: изменяет объект
public static void incrementAge(Person person) {
person.setAge(person.getAge() + 1); // Побочный эффект: изменение
}
// НЕЧИСТАЯ: обращается к БД
public static User getUserFromDatabase(int id) {
// Зависит от состояния БД, может выбросить исключение
return database.query("SELECT * FROM users WHERE id = " + id);
}
// НЕЧИСТАЯ: генерирует случайное число
public static int getRandomNumber() {
return new java.util.Random().nextInt(100);
}
}
class Person {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Сравнение: нечистая vs чистая функция
public class ComparisonExample {
// НЕЧИСТАЯ ВЕРСИЯ
private static int total = 0;
public static void addToTotalImpure(int value) {
total += value; // Изменяет глобальное состояние
System.out.println("Total: " + total); // Побочный эффект: печать
}
// ЧИСТАЯ ВЕРСИЯ
public static int calculateSum(int current, int value) {
return current + value; // Только вычисление, без побочных эффектов
}
public static void main(String[] args) {
// Нечистая функция
System.out.println("=== Impure ===");
addToTotalImpure(5); // Изменяет глобальное состояние, печатает
addToTotalImpure(3); // Зависит от предыдущего вызова
addToTotalImpure(2); // Результат зависит от истории вызовов
// Чистая функция
System.out.println("\n=== Pure ===");
int result1 = calculateSum(0, 5);
int result2 = calculateSum(result1, 3);
int result3 = calculateSum(result2, 2);
System.out.println("Result: " + result3); // 10
}
}
Характеристики чистых функций
1. Идентичные входы = идентичные выходы
public class DeterministicExample {
// ЧИСТАЯ: всегда возвращает одинаковый результат
public static int multiply(int a, int b) {
return a * b;
}
public static void main(String[] args) {
System.out.println(multiply(5, 3)); // 15
System.out.println(multiply(5, 3)); // 15
System.out.println(multiply(5, 3)); // 15
}
}
2. Нет зависимостей от внешнего состояния
public class NoExternalState {
private static double exchangeRate = 1.5; // Глобальное состояние
// НЕЧИСТАЯ: зависит от exchangeRate
public static double convertCurrency(double amount) {
return amount * exchangeRate;
}
// ЧИСТАЯ: передаём rate как параметр
public static double convertCurrencyPure(double amount, double rate) {
return amount * rate;
}
}
3. Нет изменения передаваемых объектов
public class NoObjectMutation {
// НЕЧИСТАЯ: изменяет переданный список
public static void sortImpure(java.util.List<Integer> list) {
java.util.Collections.sort(list); // Изменяет исходный список
}
// ЧИСТАЯ: возвращает новый отсортированный список
public static java.util.List<Integer> sortPure(java.util.List<Integer> list) {
return list.stream()
.sorted()
.collect(java.util.stream.Collectors.toList());
}
public static void main(String[] args) {
java.util.List<Integer> numbers = new java.util.ArrayList<>(java.util.Arrays.asList(3, 1, 2));
java.util.List<Integer> sorted = sortPure(numbers);
System.out.println("Original: " + numbers); // [3, 1, 2] — не изменилась
System.out.println("Sorted: " + sorted); // [1, 2, 3]
}
}
4. Нет побочных эффектов
public class NoSideEffects {
// НЕЧИСТАЯ: множество побочных эффектов
public static double calculateTotalImpure(java.util.List<Item> items) {
System.out.println("Starting calculation"); // Побочный эффект 1
double total = 0;
for (Item item : items) {
total += item.getPrice();
item.setProcessed(true); // Побочный эффект 2: изменение объекта
}
java.util.logging.Logger.getLogger("App").info("Total: " + total); // Побочный эффект 3
return total;
}
// ЧИСТАЯ: только вычисление
public static double calculateTotalPure(java.util.List<Item> items) {
return items.stream()
.mapToDouble(Item::getPrice)
.sum();
}
}
class Item {
private double price;
private boolean processed;
public Item(double price) {
this.price = price;
}
public double getPrice() {
return price;
}
public void setProcessed(boolean processed) {
this.processed = processed;
}
}
Преимущества чистых функций
- Тестируемость — легко тестировать, нужны только входные параметры и ожидаемый результат
- Отладка — отсутствие побочных эффектов упрощает отладку
- Повторное использование — чистые функции легче использовать повторно
- Параллелизм — можно безопасно вызывать параллельно без синхронизации
- Кеширование — результаты можно кешировать без опасений
- Рассуждения о коде — проще понять и предсказать поведение
- Оптимизация — компилятор может оптимизировать чистые функции
Пример: рефакторинг нечистой функции в чистую
import java.util.*;
public class RefactoringExample {
// НЕЧИСТАЯ версия
private static List<String> processedUsers = new ArrayList<>();
public static double calculateAverageAgeImpure(List<User> users) {
processedUsers.clear(); // Побочный эффект
double sum = 0;
for (User user : users) {
if (user.getAge() >= 18) {
sum += user.getAge();
processedUsers.add(user.getName()); // Побочный эффект
}
}
return processedUsers.isEmpty() ? 0 : sum / processedUsers.size();
}
// ЧИСТАЯ версия
public static double calculateAverageAgePure(List<User> users) {
return users.stream()
.filter(user -> user.getAge() >= 18)
.mapToDouble(User::getAge)
.average()
.orElse(0.0);
}
public static void main(String[] args) {
List<User> users = Arrays.asList(
new User("John", 25),
new User("Jane", 30),
new User("Bob", 17)
);
double average = calculateAverageAgePure(users);
System.out.println("Average age: " + average); // 27.5
}
}
class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
Best Practices
- Стремись писать чистые функции везде, где это возможно
- Передавай все необходимые параметры как аргументы
- Возвращай результаты вместо изменения состояния
- Избегай побочных эффектов (печать, запись в файл, БД)
- Используй неизменяемые данные
- Обработку побочных эффектов отдели в отдельные функции/слои
- Используй Stream API и функциональные интерфейсы
Чистые функции — это скелет хорошего, масштабируемого кода. Их использование приводит к более надёжному и поддерживаемому программному обеспечению