Рубрики
Без рубрики

Перейти к функциональности с Java

Пришло время стать функциональным программистом на Java. Помеченный функциональным, java, лямбда, функциональным интерфейсом.

Java был самым популярным языком с момента его запуска, потому что его создатели позаботились о том, чтобы язык ничего не упускал и сохранял свою оригинальность с учетом возникающих изменений.

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

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

Почему?

Разработка программного обеспечения – это итеративный процесс, который включает в себя не только написание кода, но и понимание кода, написанного другими.

Вы можете найти это сложным, и наоборот: D

Может потребоваться много времени, чтобы просто выяснить состояние конкретного объекта, если оно было изменено функциями, которые используют его в качестве аргумента. Следовательно, это затрудняет прогнозирование поведения программы.

Рассматривайте это как сторонний API без документации. Вы не уверены в том, какого поведения следует ожидать при вызове определенной функции.

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

Как?

Вся эта теория прекрасна, но как я могу использовать ее как разработчик Java?

Большинство из вас, должно быть, задумывается над этим вопросом. Ответ – новый член семейства java Лямбда .

Лямбда

Лямбда – это функция, которая действует как объект. Может быть передан в любом месте. Может быть выполнен в любое время, когда это необходимо. Может быть определен в одной строке. Может быть создан без класса. И многое другое. Это помогает удалить много шаблонного кода, ранее требуемого Java.

Syntax - (Parameter Declaration) -> {Lambda Body}
Examples -
Without parameters - () -> System.out.println("Hello lambda")
With one parameter - s -> System.out.println(s)
With two parameters - (x, y) -> x + y
With multiple line body - (x, y) -> {}

Круто, правда? но как Java узнает, какая лямбда сопоставляется с какой функцией?

Копать Глубоко

Поскольку интерфейсы являются основой лямбд. Java представила новую концепцию, известную как Функциональный интерфейс , в соответствии с которой определяется лямбда.

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

Они похожи на обычные интерфейсы в java с одним существенным отличием. Они следуют ОДНОМУ и ТОМУ ЖЕ правилу . Согласно правилу SAM, в интерфейсе допускается только один абстрактный метод (SAM).

Эта проверка может быть применена во время компиляции с помощью @Функциональный интерфейс аннотация.

Для каждой лямбды нам нужно создать новый функциональный интерфейс? Это неудобно. Верно?

Похоже, Java уже позаботилась и об этой проблеме. Пакет java.util.function содержит почти 50 функциональных интерфейсов. Крайне маловероятно, что вам может понадобиться что-то, выходящее за рамки их компетенции.

Семейство функциональных интерфейсов

Освоение всех интерфейсов может показаться многовато. Скорее, если мы просто поймем их семьи, мы сможем легко указать на правильный интерфейс.

Существует 4 семейства функциональных интерфейсов –

Потребитель – Потреблять и выбрасывать

@FunctionalInterface
public interface Consumer {
    void accept(T t);

    default Consumer andThen(Consumer after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

Он принимает объект, выполняет некоторое действие и не возвращает никаких выходных данных. Так подло: D

Пример –
Consumer stringConsumer = string -> System.out.println(string);

Поскольку лямбда-функция и println() принимают одни и те же аргументы, это также можно записать с помощью Ссылки на метод , например –

Consumer stringConsumer = System.out::println;

Функция – Отображение, Преобразование или Компьютер

@FunctionalInterface
public interface Function {
    R apply(T t);

    default  Function compose(Function before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    default  Function andThen(Function after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    static  Function identity() {
        return t -> t;
    }
}

Он принимает один объект, выполняет над ним какое-то действие и возвращает другой объект.

Пример –
Function stringStringFunction = String::toUpperCase;

Предикат – Проверка или фильтр

@FunctionalInterface
public interface Predicate {
    boolean test(T t);

    default Predicate and(Predicate other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    default Predicate negate() {
        return (t) -> !test(t);
    }

    default Predicate or(Predicate other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    static  Predicate isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

Он принимает один объект и возвращает логическое значение. Обычно определяет правила.

Пример –
Predicate stringPredicate = String::isEmpty;

Поставщик – Создать

@FunctionalInterface
public interface Supplier {
    T get();
}

Он ничего не принимает, но возвращает объект. Такой щедрый: D

Пример –
Supplier stringSupplier = () -> "Hello, World";

Реальный мир

Теперь пришло время применить наши знания к какой-нибудь реальной задаче. Давайте возьмем базовую задачу программирования от HackerRank – Футляр для верблюда .

Problem - Count number of words in a camelCase string.

Example - saveChangesInTheEditor
Result - 5

Таким образом, наш мотив состоит не только в том, чтобы решить эту проблему, но и в том, чтобы решить ее с помощью функционального способа.

Решение простое, подсчитайте количество прописных букв в строке, и результат будет равен count +1.

Чтобы сделать это функциональным способом, нам нужно –

  • Поток отдельных символов в строке и
  • Предикат, который помогает фильтровать символы верхнего регистра.
Solution -

/* Stream - s.chars()
Predicate - Character::isUpperCase */

static long camelcase(String s) {
    return s.chars().filter(Character::isUpperCase).count() + 1;
}

Ура!

Одна строка – и проблема решена.

Такой подход помогает разработчикам легко понять поведение фрагмента кода. Приведенную выше строку можно легко понять, чтобы отфильтровать символы верхнего регистра из строки и возвращать значение (количество символов + 1) без каких-либо побочных эффектов. Именно этого мы и хотели.

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

Разработчикам Java, возможно, придется приложить некоторые дополнительные усилия, чтобы освоить этот подход, потому что наши умы обучены создавать больше кода, чем требуется.:D

Оригинал: “https://dev.to/ajiteshtiwari/go-functional-with-java-45ac”