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

  1. Что такое функциональный интерфейс
  2. Лямбда vs анонимные классы
  3. Встроенные функциональные интерфейсы
  4. Бинарные специализации функциональных интерфейсов
  5. Примитивные специализации функциональных интерфейсов

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

Чтобы иметь возможность использовать лямбда выражения, необходим интерфейс:

public interface Searchable {
    boolean test(Car car);
}
Searchable s = (Car c) -> c.getCostUSD() > 20000; 

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

Тип выражения выводится из контекста, в котором используется лямбда выражение. Этот тип называется целевой тип (target type).

Если лямбда выражение присваивается какому-то интерфейсу, лямбда выражение должно соответствовать синтаксису метода интерфейса.

Одно и то же лямбда выражение может использоваться с разными интерфейсами, если они имеют абстрактные методы, которые совместимы. Например:

interface Searchable {
     boolean test(Car car);
}
interface Saleable {
     boolean approve(Car car);
}
//...
Searchable s1 = c -> c.getCostUSD() > 20000;
Saleable s2 = c -> c.getCostUSD() > 20000;

Функциональный интерфейс (functional interface) – это интерфейс у которого только один абстрактный метод. Функциональный интерфейс может содержать любое количество методов по умолчанию (default) или статических методов. Например:

interface A {
     default int defaultMethod() {
         return 0;
     }
     void method();
}

Еще один пример функционального интерфейса:

interface B {
     default int defaultMethod() {
         return 0;
     }
     default int anotherDefaultMethod() {
         return 0;
     }
     void method();
}

Функциональный интерфейс может содержать методы класса Object:

interface A {
     boolean equals(Object o);
     int hashCode();
     String toString();
     void method();
}

В Java 8 была введена аннотация @FunctionalInterface, которая генерирует ошибку компиляции, если интерфейс не является функциональным:

// This won't compile
@FunctionalInterface 
    interface A {
     void m(int i);
     void m(long l);
}

Примеры функциональных интерфейсов: java.lang.Runnable, java.util.Comparator.

2. Лямбда vs анонимные классы

Лямбда выражения являются альтернативой анонимным классам. Но они не одинаковы.

Общее:

  1. Локальные переменные могут быть использованы только если они final или effective final.
  2. Разрешается доступ к переменным класса и статическим переменным класса.
  3. Они не должны выбрасывать больше исключений чем определено в throws метода функционального интерфейса.

Различия:

  1. Для анонимных классов ключевое слово this ссылается на сам класс. Для лямбда выражений на внешний класс.
  2. Анонимные классы компилируются во внутренние классы. Но лямбда выражения преобразуются в статические private методы класса, в котором они используют invokedynamic инструкцию. Лямбда более эффективны, так как не надо загружать еще один класс.

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

В Java 8 добавлены встроенные функциональные интерфейсы в пакет java.util.function:

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

4. Бинарные специализации функциональных интерфейсов

Существуют бинарные специализации (binary specializations) интерфейсов Predicate, Consumer, Function и UnaryOperator, которые принимают на вход два элемента.

Интерфейс Функциональный дескриптор Описание
BiConsumer<T, U>
(T, U) -> void
Представляет операцию, которая принимает на вход два элемента и не возвращает результат.
BiFunction<T, U, R>
(T, U)  -> R
Представляет операцию, которая принимает на вход два элемента и возвращает результат.
BiPredicate<T, U>
(T, U) -> boolean
Представляет операцию, которая принимает на вход два элемента и возвращает значение boolean.
BinaryOperator<T>
(T, T) -> T
Представляет операцию, которая принимает на вход два элемента одного типа и возвращает значение того же типа.

5. Примитивные специализации функциональных интерфейсов

5.1. Predicate

Интерфейс Описание
IntPredicate
Принимает на вход значение типа int возвращает значение типа boolean.
LongPredicate
Принимает на вход значение типа long возвращает значение типа boolean.
DoublePredicate
Принимает на вход значение типа double возвращает значение типа boolean.

5.2. Consumer

Интерфейс Описание
IntConsumer

Принимает на вход значение типа int и ничего не возвращает.

LongConsumer

Принимает на вход значение типа long и ничего не возвращает.

DoubleConsumer
Принимает на вход значение типа double и ничего не возвращает.

5.3. Function

Принимающие примитивные специализации

Интерфейс Описание
IntFunction<R>

Принимает на вход значение типа int и возвращает значение типа R.

LongFunction<R>
Принимает на вход значение типа long и возвращает значение типа R.
DoubleFunction<R>
Принимает на вход значение типа double и возвращает значение R.

Производящие примитивные специализации

Интерфейс Описание
ToIntFunction<T>

Принимает на вход значение типа T и возвращает значение типа int.

ToDoubleFunction<T>

Принимает на вход значение типа T и возвращает значение типа double.

ToLongFunction<T>

Принимает на вход значение типа T и возвращает значение типа long.

Принимающие  и производящие примитивные специализации

Интерфейс Описание
IntToDoubleFunction
Принимает на вход значение типа int и возвращает значение типа double.
IntToLongFunction
Принимает на вход значение типа int и возвращает значение типа long.
LongToDoubleFunction
Принимает на вход значение типа long и возвращает значение типа double.
LongToIntFunction
Принимает на вход значение типа long и возвращает значение типа int.
DoubleToIntFunction
Принимает на вход значение типа double и возвращает значение типа int.
DoubleToLongFunction
Принимает на вход значение типа double и возвращает значение типа long.

5.4. Supplier

Интерфейс Описание
BooleanSupplier

Возвращает значение типа boolean.

IntSupplier
Возвращает значение типа int.
LongSupplier
Возвращает значение типа long.
DoubleSupplier
Возвращает значение типа double.

5.5. UnaryOperator

Интерфейс Описание
IntUnaryOperator
Принимает и возвращает значение типа int.
LongUnaryOperator
Принимает и возвращает значение типа long.
DoubleUnaryOperator
Принимает и возвращает значение типа  double.

5.6. BinaryOperator

Интерфейс Описание
IntBinaryOperator
Принимает на вход два значения типа int и возвращает значение типа int.
LongBinaryOperator
Принимает на вход два значения типа long и возвращает значение типа long.
DoubleBinaryOperator
Принимает на вход два значения типа double и возвращает значение типа double.

5.7. BiConsumer

Интерфейс Описание
ObjIntConsumer<T>
Принимает на вход два значения: типа int и Object.
ObjLongConsumer<T>
Принимает на вход два значения: типа long и Object.
ObjDoubleConsumer<T>
Принимает на вход два значения: типа double и Object.  

5.8. BiFunction

Интерфейс Описание
ToIntBiFunction<T, U>
Принимает на вход два объекта и возвращает значение типа int.
ToLongBiFunction<T, U>
Принимает на вход два объекта и возвращает значение типа long.
ToDoubleBiFunction<T, U>
Принимает на вход два объекта и возвращает значение типа double. 
Read also:
Comments