Для чего нужны функциональные интерфейсы в 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 шт):

Автор решения: Byb

Существует так называемый функциональный подход к программированию, при котором мы можем оперировать методами (функциями) как переменными.

Есть языки программирования, в которые такая возможность встроена непосредственно, на синтаксическом уровне: они называются функциональными. К таким языкам относится, например, Haskell.

В языках C и C++ можно использовать ссылки на методы, причём не те, которые в Java (об этом далее), а настоящие ссылки.

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

Когда мы пишем что-то вроде

(car) -> car.getOwner();

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

Упомянутая вами ссылка на метод - это просто способ ещё более коротко и изящно записать лямбда-выражение. Например, приведённая выше строка могла бы выглядеть так:

Car::getOwner

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

Очень грустно было бы для метода map() объявлять отдельный класс, имплементирующий целевой интерфейс и содержащий один простой метод, а потом в методе map() создавать объект от него с целью вызова этого самого метода.

Гораздо лучше написать короткую лямбду, а, если ситуация позволяет, то и вовсе ссылку на метод. И пусть это не чистое функциональное программирование, а скорее некая его реализация за счёт ООП, пользоваться этим всё равно удобно.

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


Когда стоит применять функциональный интерфейс, а когда - функцию?

Лямбда-выражение нужно использовать только в том случае, когда для неёго есть контекст: соответствующий функциональный интерфейс. Для примера возьмём всё тот же метод map() в Stream API. Этот метод ждёт на вход следующий аргумент:

Function<? super T, ? extends R> mapper

Соответственно, простой метод здесь не применить. Можно либо (зачем-то) создать объект от полноценного отдельного класса, имплементирующего Function<>, либо, что более логично, задействовать лямбду.

Во всех остальных случаях используются обычные методы. То есть, лямбда-выражения применяются только тогда, когда в них есть смысл.

→ Ссылка