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

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

Пример руководства по функциональным интерфейсам Java 8. @Функциональный интерфейс на java. Пример учебника по функциональному интерфейсу Java с примерами лямбда-выражений.

Автор оригинала: Pankaj Kumar.

Добро пожаловать в учебник по примерам функциональных интерфейсов Java 8. Java всегда была Объектно – ориентированным языком программирования . Что означает, что все в программировании на Java вращается вокруг объектов (за исключением некоторых примитивных типов для простоты). У нас есть не только функции в java, они являются частью класса, и нам нужно использовать класс/объект для вызова любой функции.

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

Если мы рассмотрим некоторые другие языки программирования, такие как C++, JavaScript; они называются функциональным языком программирования , потому что мы можем писать функции и использовать их при необходимости. Некоторые из этих языков поддерживают объектно-ориентированное программирование, а также функциональное программирование.

Быть объектно-ориентированным неплохо, но это привносит в программу много многословия. Например, предположим, что нам нужно создать экземпляр Runnable. Обычно мы делаем это с помощью анонимных классов, как показано ниже.

Runnable r = new Runnable(){
			@Override
			public void run() {
				System.out.println("My Runnable");
			}};

Если вы посмотрите на приведенный выше код, фактическая часть, которая может быть использована, – это код внутри метода run (). Остальная часть кода связана с тем, как структурированы java-программы.

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

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

Интерфейс с ровно одним абстрактным методом называется функциональным интерфейсом. @Functional interface добавлена аннотация, чтобы мы могли пометить интерфейс как функциональный интерфейс.

Его не обязательно использовать, но лучше всего использовать его с функциональными интерфейсами, чтобы случайно не добавлять дополнительные методы. Если интерфейс аннотирован аннотацией @Functional Interface и мы пытаемся использовать более одного абстрактного метода, это приводит к ошибке компилятора.

Основное преимущество функциональных интерфейсов java 8 заключается в том, что мы можем использовать лямбда-выражения для их создания и избегать использования громоздкой реализации анонимного класса.

API коллекций Java 8 был переписан и представлен новый потоковый API, который использует множество функциональных интерфейсов. Java 8 определила множество функциональных интерфейсов в java.util.function пакете. Некоторые из полезных функциональных интерфейсов java 8 являются Потребителем , Поставщиком , Функцией и Предикатом .

Вы можете найти более подробную информацию о них в примере потока Java 8 .

ява.ланг.Запускаемый является отличным примером функционального интерфейса с одним абстрактным методом run() .

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

interface Foo { boolean equals(Object obj); }
// Not functional because equals is already an implicit member (Object class)

interface Comparator {
 boolean equals(Object obj);
 int compare(T o1, T o2);
}
// Functional because Comparator has only one abstract non-Object method

interface Foo {
  int m();
  Object clone();
}
// Not functional because method Object.clone is not public

interface X { int m(Iterable arg); }
interface Y { int m(Iterable arg); }
interface Z extends X, Y {}
// Functional: two methods, but they have the same signature

interface X { Iterable m(Iterable arg); }
interface Y { Iterable m(Iterable arg); }
interface Z extends X, Y {}
// Functional: Y.m is a subsignature & return-type-substitutable

interface X { int m(Iterable arg); }
interface Y { int m(Iterable arg); }
interface Z extends X, Y {}
// Not functional: No method has a subsignature of all abstract methods

interface X { int m(Iterable arg, Class c); }
interface Y { int m(Iterable arg, Class c); }
interface Z extends X, Y {}
// Not functional: No method has a subsignature of all abstract methods

interface X { long m(); }
interface Y { int m(); }
interface Z extends X, Y {}
// Compiler error: no method is return type substitutable

interface Foo { void m(T arg); }
interface Bar { void m(T arg); }
interface FooBar extends Foo, Bar {}
// Compiler error: different signatures, same erasure

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

Лямбда – выражения-это способ, с помощью которого мы можем визуализировать функциональное программирование в объектно-ориентированном мире java. Объекты являются основой языка программирования java, и у нас никогда не может быть функции без объекта, поэтому язык Java поддерживает использование лямбда-выражений только с функциональными интерфейсами.

Поскольку в функциональных интерфейсах есть только одна абстрактная функция, нет никакой путаницы в применении лямбда-выражения к методу. Синтаксис лямбда-выражений: (аргумент) – > (тело) . Теперь давайте посмотрим, как мы можем написать выше анонимное выполняемое с использованием лямбда-выражения.

Runnable r1 = () -> System.out.println("My Runnable");

Давайте попробуем понять, что происходит в приведенном выше лямбда-выражении.

  • Запускаемый-это функциональный интерфейс, поэтому мы можем использовать лямбда-выражение для создания его экземпляра.
  • Поскольку метод run() не принимает аргументов, наше лямбда-выражение также не имеет аргументов.
  • Как и в блоках if-else, мы можем избегать фигурных скобок ({}), поскольку в теле метода есть один оператор. Для нескольких операторов нам пришлось бы использовать фигурные скобки, как и любые другие методы.

Зачем нам нужно Лямбда-выражение

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

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

    Чтобы объяснить это, давайте возьмем простой пример, где нам нужно написать метод для проверки того, является ли переданное число простым числом или нет.

    Традиционно мы бы написали его код, как показано ниже. Код не полностью оптимизирован, но хорош для примера, так что потерпите меня в этом.

    Проблема с приведенным выше кодом заключается в том, что он носит последовательный характер, если число очень велико, то это займет значительное количество времени. Еще одна проблема с кодом заключается в том, что существует так много точек выхода, и он не читается. Давайте посмотрим, как мы можем написать тот же метод, используя лямбда-выражения и потоковый API.

    IntStream -это последовательность примитивных элементов со значением int, поддерживающих последовательные и параллельные агрегатные операции. Это примитивная специализация int Stream .

    Для большей удобочитаемости мы также можем написать метод, как показано ниже.

    Если вы не знакомы с IntStream, метод range() возвращает последовательный упорядоченный IntStream от начального включительно (включительно) до конечного (исключающего) с шагом 1.

    метод none Match() возвращает, соответствуют ли элементы этого потока указанному предикату. Он может не оценивать предикат по всем элементам, если это не необходимо для определения результата.

  3. Передача поведения в методы

    Давайте посмотрим, как мы можем использовать лямбда-выражения для передачи поведения метода на простом примере. Допустим, нам нужно написать метод для суммирования чисел в списке, если они соответствуют заданным критериям. Мы можем использовать предикат и написать метод, как показано ниже.

    Пример использования:

  4. Более высокая эффективность при лени

    Еще одним преимуществом использования лямбда-выражения является ленивая оценка, например, предположим, что нам нужно написать метод, чтобы узнать максимальное нечетное число в диапазоне от 3 до 11 и вернуть его квадрат.

    Обычно мы пишем код для этого метода следующим образом:

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

    Если вас удивляет оператор с двойным двоеточием (::), он введен в Java 8 и используется для ссылок на методы . Компилятор Java заботится о сопоставлении аргументов вызываемому методу. Это короткая форма лямбда-выражений i - > больше 3(i) или i -> Число Test.is Больше 3(i) .

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

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

() -> {}                     // No parameters; void result

() -> 42                     // No parameters, expression body
() -> null                   // No parameters, expression body
() -> { return 42; }         // No parameters, block body with return
() -> { System.gc(); }       // No parameters, void block body

// Complex block body with multiple returns
() -> {
  if (true) return 10;
  else {
    int result = 15;
    for (int i = 1; i < 10; i++)
      result *= i;
    return result;
  }
}                          

(int x) -> x+1             // Single declared-type argument
(int x) -> { return x+1; } // same as above
(x) -> x+1                 // Single inferred-type argument, same as below
x -> x+1                   // Parenthesis optional for single inferred-type case

(String s) -> s.length()   // Single declared-type argument
(Thread t) -> { t.start(); } // Single declared-type argument
s -> s.length()              // Single inferred-type argument
t -> { t.start(); }          // Single inferred-type argument

(int x, int y) -> x+y      // Multiple declared-type parameters
(x,y) -> x+y               // Multiple inferred-type parameters
(x, final y) -> x+y        // Illegal: can't modify inferred-type parameters
(x, int y) -> x+y          // Illegal: can't mix inferred and declared types

Ссылки на методы и конструкторы

Ссылка на метод используется для ссылки на метод без его вызова; ссылка на конструктор аналогично используется для ссылки на конструктор без создания нового экземпляра именованного класса или типа массива.

Примеры ссылок на методы и конструкторы:

System::getProperty
System.out::println
"abc"::length
ArrayList::new
int[]::new

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

Вам также следует ознакомиться с функциями Java 8, чтобы узнать обо всех улучшениях и изменениях в выпуске Java 8.