вторник, 28 марта 2017 г.

Функциональное программирование в Java 8 (Часть 1): Функция как объект

После прочтения Части 0 вы поняли, что функциональное программирование - это круто, сегодня мы сделаем наш первый большой шаг в данной теме. С выходом Java 8 функции  стали функциями первого класса. А это значит, что вы можете передавать функцию как параметр другой функции, возвращать функцию и хранить функцию как объект.

Почему я должен хранить функцию как объект?

  1. Создавать "super private" функции. Как вы знаете, качество кода очень важно. По этому мы используем private функции, чтобы снизить число открытых методов класса. Мы не хотим показывать основной код пользователям, им достаточно работать только с открытыми методами объекта. Но что, если мы хотим создать функцию, которая доступа только одному методу во всем классе? Если хранить функцию первого класса ,как объект, который будет доступен только одному методу, мы сможем этого добиться.
  2. Улучшить паттерны проектирования. Если вы когда-либо работали над большим проектом, то знаете, каким запутанным он может быть. По-этому были изобретены паттерны проектирования. Один из самых прикольных - паттерн стратегия. Я напишу подробный пост об этом позже, но основная мысль заключается в том, что он переключает похожие алгоритмы, в зависимости от параметра. Для каждого алгоритма необходимо написать свой класс, который имплементирует интерфейс. Но когда вы можете хранить функцию как объект, то вам нужна только одна функция-объект для каждого алгоритма. Это позволяет создавать меньше кода и делает его понятнее.
  3. Создавать функции "Высшего порядка". Теперь самое прикольное. Вы можете использовать каждый объект в качестве параметра метода. Почему бы не вызвать метод с аргументом функции? Методы, которые принимают функцию как параметр или возвращают ее, называются функциями более высокого порядка. Прежде чем я смогу привести вам пример, мы должны научиться хранить функцию в объекте.

Хранение функции в объекте.

В Java 8 был представлю интерфейс Interface Function<T,R>. Он может хранить функцию, которая принимает один аргумент и возвращает объект. Generic T является типом аргумента, а R - типом объекта, который вы возвращаете.

Пример: Функция вычисления.

Это очень простой пример функции высшего порядка. Он принимает функцию и Integer и вычисляет данную функцию с помощью Integer.

public static Integer compute(Function<Integer, Integer> function, Integer value) {
    return function.apply(value);
}
А теперь мы хотим использовать эту функцию, чтобы инвертировать число.
public class AwesomeClass {
    private static Integer invert(Integer value) {
        return -value;
    }
    public static Integer invertTheNumber(){
        Integer toInvert = 5;
        Function<Integer, Integer> invertFunction = AwesomeClass::invert;
        return compute(invertFunction, toInvert);
    }
    
}
Здесь два интересных момента, первый из них:
return function.apply(value);
Вызов метода объекта-функции, просто используются аргументы и возвращается результат метода. Для нашего примера необходимо написать:
return invert(value);
Второй интересный момент:
Function<Integer, Integer> invertFunction = AwesomeClass::invert;
То, что мы здесь используем называется ссылкой на метод. Мы делаем из метода invert() объект-функцию, используя оператор ::. Это один из двух способов хранить функцию, как объект. Но этот код ничего не упощает. Его можно изменить:
public class AwesomeClass {
    private static Integer invert(Integer value) {
        return -value;
    }
    public static Integer invertTheNumber(){
        Integer toInvert = 5;
        return invert(toInvert);
    }    
}
Такое решение не нуждается в функции compute, да и в самом ФП тоже. Чтобы сделать ФП подходящим в данном примере, нужно представить второй способ хранения функции как объект. Он опирается на анонимные функции или так называемые лямбды.

Как работать с лямбдами

Чтобы работать с лямбдами в Java 8 необходимо разобраться с новым синтаксисом, чтоб работать с ними (Лямбдами) правильно.

Пример: сложение двух Integer

В старой-доброй Java7 вы могли написать метод, чтобы сложить два числа:
public Integer add(Integer a, Integer b) {
    return a + b;
}
А это Java 8 c лямбдами:
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
Достаточно просто, не так ли? BiFunction - это еще один интерфейс из java.util, чтобы представить функцию с двумя входными параметрами и одним выходным.В скобках указываются входные параметры. Вы не обязаны указывать их тип. Необходимо только сказать сколько их будет и как они называются. В Java 7 это эквивалентно:
(Integer a, Integer b)
Следующее -> стрелка. Она соответствует фигурным скобочкам и отделяет голову функции от тела. После стрелки можно работать с входными параметрами. Если у функции всего одно вычисление - слово return не нужно, будет возвращен результат этого вычисления. Конечно, тело функции можно сделать больше, добавив фигурные скобки:
BiFunction<Integer, Integer, Integer> add = (a,b) -> {
    Integer result = a + b;
    return result;
};

Но чаще всего, нужна только одна строка, по-этому фигурные скобки и ключевое слово return не нужны.

Улучшение функции вычисления

Учитывая выше сказанное, можно провести рефакторинг:
public class AwesomeClass {
    public static Integer invertTheNumber(){
        Integer toInvert = 5;
        return compute((a) -> -a, toInvert);
    }
}
Вот это уже красивый код! Мы можем реализовать нашу функцию инвертирования через лямбды. Это делает наш код красивее, чем старая ФП версия и версия с Java7. Нам не нужно создавать дополнительные методы для инвертирования Integer, мы просто используем небольшую лямбду, которая все реализует.

Заключение

На сегодня все. Мы сделали первые крупные шаги в направлении ФП на Java 8. Прежде всего, мы увидели много преимуществ ФП. После этого мы использовали нашу первую функцию в качестве параметра в другом методе, используя ссылки на методы и лямбду (анонимные функции). Во второй части мы расскажем о Optionals и о том, как с ними работать.
 Спасибо за чтение и хорошего дня,
 Никлас

PS это мой перевод данной статьи

Комментариев нет :

Отправить комментарий