Функціональне програмування в Java Ця стаття про:

Про застосування функціонального стилю програмування в мові Java. Про деякі базові патерни для роботи з колекціями даних з функціонального програмування в прикладах на Java. Про бібліотеку Google Collections. Якщо ви програмуєте на мовах Java, C #, C + +, PHP, або будь-якому іншому ГО мовою, хотіли б познайомитися з функціональним програмуванням, але не має можливості / бажання вивчати Haskell / Scala / Lisp / Python, - ця стаття спеціально для вас.

Тим, хто знайомий з функціональним програмуванням, але ніколи не застосовував його в Java, думаю, це буде теж цікаво. Конструкція «функція» в мові Java Що таке функціональне програмування? Якщо в двох словах, то функціональне програмування - це програмування, в якому функції є об'єктами, і їх можна присвоювати змінним, передавати в якості аргументів інших функцій, повертати як результат від функцій і т. п. Переваги, які розкриває така можливість, будуть зрозумілі трохи пізніше. Поки нам треба розібратися, як в Java можна використовувати саму конструкцію «функція».

Як відомо, в Java немає функцій, там є тільки класи, методи та об'єкти класів. Зате в Java є анонімні класи, тобто класи без імені, які можна оголошувати прямо в коді будь-якого методу. Цим ми і скористаємося. Для початку оголосимо такий інтерфейс: public final interface Function<F, T>

T apply(F from);

Тепер в коді якогось методу ми можемо оголосити анонімну реалізацію цього інтерфейсу: public static void main() { // Оголошуємо "функцію", присвоюємо її змінну intToString. Function<Integer, String> intToString = new Function<Integer, String>() { @Override public String apply(Integer from) { return from.toString();

intToString.apply(9000); // Викликаємо нашу функцію. Отримуємо рядок "9000".

Таку реалізацію ми й будемо називати «анонімної функцією». З точки зору функціонального програмування з нею можна робити все те ж саме, що і з функцією з функціональних мов: присвоювати змінним, передавати в якості аргументу інших функцій (і методам класів), одержувати в якості результату від функцій (і методів класів).

Тепер можна перейти до викладу деяких базових патернів функціонального програмування.

Робота з колекціями у функціональному стилі Припустимо, у нас є якась колекція цілих чисел. Ми хочемо їх вивести на екран у вигляді рядка, і кожне число в рядку буде розділено через кому. Нефункціональне рішення виглядало б приблизно так: public String joinNumbers(Collection<? extends Integer> numbers) { StringBuilder result = new StringBuilder(); boolean first = true; for (Integer number : numbers) { if (first) first = false; else result.append(", "); result.append(number);

return result.toString();

Для реалізації функціонального рішення нам буде потрібно спершу підготувати кілька функцій і методів. Будемо оголошувати їх як статичні поля класу:

public static final Function<Integer, String> INT_TO_STRING = ... // Вже реалізували вище // Бере поелементно значення з колекції from, перетворює їх за допомогою функції<br /> transformer // і повертає список результатів перетворення в тому ж порядку. public static <F, T> List<T> map(Collection<F> from, Function<? super F,? extends T> <br /> transformer)

ArrayList<T> result = new ArrayList<T>(); for (F element : from) result.add(transformer.apply(element)); return result;

// Бере колекцію довільних елементів і конкатенірует їх в рядок public static <T> String join(Collection<T> from, String separator) { StringBuilder result = new StringBuilder(); boolean first = true; for (T element : from) { if (first) first = false; else result.append(separator); result.append(element);

return result.toString();

Тепер наш метод joinNumbers буде виглядати наступний чином: public String joinNumbers(Collection<? extends Integer> numbers) { return join(map(numbers, INT_TO_STRING), ", ");

Метод реалізований рівно в один простий рядок. public String joinNumbers(Collection<? extends Integer> numbers) { return join(map(numbers, INT_TO_STRING), ", ");

Робота з колекціями за допомогою Google Collections Google якраз створили зручну бібліотеку з утілітнимі класами, що дозволяє працювати з колекціями в Java у функціональному стилі. Ось деякі з можливостей, які вона надає:

Interface Function <F, T>. Інтерфейс, аналогічний наведений вище. Iterables.filter. Бере колекцію і функцію-предикат (функцію, яка повертає булево значення). У відповідь повертає колекцію, яка містить усі елементи вихідної, на які зазначена функція повернула true. Зручно, наприклад, якщо ми хочемо відсіяти з колекції всі парні числа: Iterables.filter (numbers, IS_ODD); Iterables.transform. Робить те ж саме, що функція map в прикладі вище. Functions.compose. Бере дві функції. Повертає нову функцію - їх композицію, тобто функцію, яка отримує елемент, подає його в другу функцію, результат подає в першу функцію, і отриманий з першої функції результат повертає користувачу. Композицію можна використовувати, наприклад, так: Iterables.transform (numbers, Functions.compose (INT_TO_STRING, MULTIPLY_X_2)); У Google Collections звичайно є ще багато інших корисних речей як для функціонального програмування, так і для роботи з колекціями в імперативному стилі.

Дивіться також Освоюємо Java