Метод за замовчуванням (default method) - не статичний метод з реалізацією оголошеною в тілі інтерфейсу. Для оголошення використовується ключове слово default.
- Якщо клас імплементує інтерфейс, він може, але не зобов'язаний імплементувати дефолтні методи, клас наслідує реалізацію за замовчуванням. Якщо клас перевизначає метод, то береться імплементація з класу, якщо ні то з інтерфейсу.
- Використовується якщо потрібно розширити існуючі інтерфейси новими методами, або розширити функціональність не порушуючи зворотню сумісність.
- Інтерфейси при наслідуванні один-одного можуть перевизначати дефолтні методи.
- Якщо клас імплементує одночасно два інтерфейси з онаковими дефолтними методами, виникає проблема множинного наслідування. Якщо ж клас імплементує інтерфейс, і наслідує інший клас, в яких є однакові методи, перевага надається методу класу.
Логічним розвитком дефолтних методів є private methods в java 1.9. У них описуються блоки коду, які не бажано розміщувати в дефолтних методах (приховування логіки), і які не потрібні в інтерфейсах і класах, які будуть реалізувати інтерфейс. Приватні методи не наслідуються.
Функціональний інтерфейс - інтерфейс в якого є тільки один абстрактний метод. Крім єдиного абстрактного методу може також містити static методи, default методи, та методи класу Object. Основне призначення - використання в лямбда виразах. Також функціональні інтерфейси реалізують аналог функцій вищого порядку (функцій, які в якості аргументів можуть приймати інші функції).
Щоб перевірити, чи є інтерфейс функціональним на етапі компіляції, використовується анотація @FunctionalInterface
Посилання на методи в Java 1.8 з'явився механізм, що дозволяє посилатися на метод в реалізації функціонального інтерфейсу. Для цього був введений новий оператор ::.
- Predicate<T> - приймає об'єкт типу Т і перевіряє деяку умову. Якщо умова виконується, повертає
true. - Consumer<T> - приймає об'єкт типу Т, виконує деяку дію, але при цьому нічого не повертає.
- Function<T,R> - приймає об'єкт типу Т, перетворює його на об'єкти типу R, і повертає об'єкт типу R.
- Supplier<T> - не приймає жодних аргументів, але при цьому повертає об'єктитипу Т.
- UnaryOperator<T> - приймає об'єкт типу Т, виконує над тим деякі операції, і повертає об'єкт того ж типу.
- BinaryOperator<T> - приймає як параметри 2 об'єкти типу Т, виконує над ними деякі операції, і повертає об'єкт типу Т.
Predicate - функціональний інтерфейс, що з'явився у java 8. Використовується для перевірки тої чи іншої умови. Найчастіше використовується для фільтрації даних, щоб визначити, підійде об'єкт для подальшої роботи, чи ні.
Predicate містить наступні методи:
- абстрактний
boolean test(t t) - default
and(), or(), negate()(логічні оператори) - static
not(), isEqual()
List<Integer> listInt = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
Predicate<Integer> p1 = e -> e > 2;
Predicate<Integer> p2 = e -> e < 5;
listInt.removeIf(p1.and(p2).negate()); // або listInt.removeIf(Predicate.not(p1.and(p2)));
System.out.println(listInt); // [3, 4]
Спеціалізації інтерфейсу predicate:
- BiPredicate<T,U> приймає на вхід 2 об'єкти, має ті ж методи що й Predicate крім статичних
- IntPredicate, LongPredicate, DoublePredicate - примітивні спеціалізації інтерфейсу, створені для роботи з відповідними примітивами.
Function<T,R> - функціональний інтерфейс, що з'явився у java 8. Використовується для опису функції, що генерує на основі об'єкту одного типу об'єкт іншого типу (або того ж типу). Використовується в методах створення одних об'єктів на основі інших, або для перетворення одних типів в інші.
Методи Function:
- абстрактний
R apply(T t) - default
andThen(), compose()(композиція) - static
identity()(повертає значення аргументу)
Function<String, Integer> f = String::length;
System.out.println(f.apply("Hello")); //5
Спеціалазації інтерфейсу Function:
- BiFunction<U,V,R> як і у випадку зі спеціалізацією predicate приймає на вхід 2 об'єкти.
- IntFunction, LongFunction ... примітивні спеціалізації, що на вхід приймають об'єкт відповідного примітивного типую
- ToIntFunction, ToLongFunction ... породжуючі спеціалізації, що повертають об'єкт відповідного примітивного типу
Consumer - функціональний інтерфейс java8, що використовується для опису операції над об'єктом, без повернення результату. В основному реалізується опираючист на "побічні ефекти" при виконанні операції.
Побічні ефекти методі (функції) - можливість функції в процесі виконання:
- читати і модифікувати значення глобальних змінних і полів класу.
- виконуватиоперації вводу/виводу
- реагувати на виключення і викликати їх обробку
Методі consumer:
- абстрактний
void accept(T t) - дефолтний
Consumer<T> andThen(Consumer<? super T> after)(композиція).
Як і для інших функціональних інтерфейсів, для consumer існують бінарна та примітивна спеціалізації:
void accept(T t, U u)default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after)- ...
UnaryOperator - функціональний інтерфейс java8. Є наслідником функціонального інтерфейсу Function<T,T>, з тою лиш різницею, що параметр і результат того ж типу.
Методи UnaryOperator:
abstract T apply(T t)default <V> Function<V,T> andThen(Function<? super T, ? extends V> after)default V Function<V,T> compose(Function<? super V, ? extends T> before)(композиція)static <T> UnaryOperator<T> identity()(повертає значення аргументу)
UnaryOperator має стандартні примітивні спеціалізації.
BinaryOperator - функціональний інтерфейс в java8. Є наслідником функціонального інтерфейсу BiFunction<T,T,T>. Описує операцію над двома операндами одного типу і повертає результат того ж типу.
Методи BinaryOperator схожі на методb Function:
abstract T apply(T t1, T t2)default V BiFunction<T,T,V> andThen(Function<? super T, ? extends V> after)(композиція)static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator), аналогічноmaxBy().
BinaryOperator має стандартні примітивні спеціалізації.
Supplier - функціональний інтерфейс в java8. Використовується для створення об'єктів потрібного типу без параметрів. Часто використовується в термінальних методах Stream API.
Має єдиний абстрактний метод T get().
Часто Supplier використовують в якості посилання на конструктор класу, реалізуючи т.зв "ліниве" створення об'єкту. Об'єкт створюється лише тоді, коли викликаєтьяс метод get() в реалізації інтерфейсу. Якщо метод не викликається, об'єкт не створюється.
Supplier<List<String>> sup = ArrayList::new;
List<String> list = sup.get();