Для чего нужны функциональные интерфейсы в Java?
Совсем недавно начал изучать программирование и наткнулся на тему лямбда-выражений. На протяжении изучения никак не мог понять зачем нужны функциональные интерфейсы, если вместо них можно использовать обычную функцию? Пример:
public static void main(String[] args) {
lol operation = (x,y)->x+y;
System.out.println(operation.printLol(2,3));
System.out.println(sum(2,3));
}
interface lol {
int printLol(int x, int y);
}
public static long sum(int x, int y){
return x+y;
}
Так в чем отличие функции от функционального интерфейса? Когда стоит применять функциональный интерфейс, а когда - функцию?
Ответы (1 шт):
Существует так называемый функциональный подход к программированию, при котором мы можем оперировать методами (функциями) как переменными.
Есть языки программирования, в которые такая возможность встроена непосредственно, на синтаксическом уровне: они называются функциональными. К таким языкам относится, например, Haskell.
В языках C и C++ можно использовать ссылки на методы, причём не те, которые в Java (об этом далее), а настоящие ссылки.
В языке же Java нет ни того, ни другого, поэтому функциональное программирование в ней осуществляется посредством функциональных интерфейсов.
Когда мы пишем что-то вроде
(car) -> car.getOwner();
нам кажется, будто мы действительно объявляем функцию на месте и программируем в функциональном стиле. Но на деле для такой записи создаётся анонимный класс, расширяющий целевой интерфейс и реализующий метод в соответствии с тем, что написано после стрелки.
Упомянутая вами ссылка на метод - это просто способ ещё более коротко и изящно записать лямбда-выражение. Например, приведённая выше строка могла бы выглядеть так:
Car::getOwner
Для чего в целом всё это нужно - я думаю, тут можно продолжить пример, строки кода для которого я написал выше. Есть у нас список автомобилей, и нам нужно его преобразовать в список владельцев. К примеру, используя Stream API.
Очень грустно было бы для метода map()
объявлять отдельный класс, имплементирующий целевой интерфейс и содержащий один простой метод, а потом в методе map()
создавать объект от него с целью вызова этого самого метода.
Гораздо лучше написать короткую лямбду, а, если ситуация позволяет, то и вовсе ссылку на метод. И пусть это не чистое функциональное программирование, а скорее некая его реализация за счёт ООП, пользоваться этим всё равно удобно.
Использование лямбда-выражений сокращает и упрощает код, а также во многих случаях делает его более читаемым и выразительным.
Когда стоит применять функциональный интерфейс, а когда - функцию?
Лямбда-выражение нужно использовать только в том случае, когда для неёго есть контекст: соответствующий функциональный интерфейс. Для примера возьмём всё тот же метод map()
в Stream API. Этот метод ждёт на вход следующий аргумент:
Function<? super T, ? extends R> mapper
Соответственно, простой метод здесь не применить. Можно либо (зачем-то) создать объект от полноценного отдельного класса, имплементирующего Function<>
, либо, что более логично, задействовать лямбду.
Во всех остальных случаях используются обычные методы. То есть, лямбда-выражения применяются только тогда, когда в них есть смысл.