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

Последнее обновление: 02.10.2025

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

В языке Java есть особый тип интерфейсов, который называется функциональным интерфейсом. Функциональный интерфейс - это интерфейс, в котором определен только один абстрактный метод (метод без реализации). Простейший пример:

interface Message{
    void print();
}

Данный интерфейс Message можно назвать функциональным.

И как и в случае с любым интерфейсом, мы можем определить переменную этого интерфейса:

Message mes;

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

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

Ссылка на метод (method reference) представляет синтаксическую конструкцию следующего вида:

имя_класса::имя_статического_метода     // если метод статический

объект_класса::имя_метода       // если метод нестатический

Например:

public class Program {

    public static void main(String[] args) {
        
        Message mes;
        mes = Program::hello;
    }

    static void hello(){
        System.out.println("Hello METANIT.COM");
    }
}
interface Message{
    void print();
}

Здесь переменной mes передаем ссылку на метод hello, который определен в классе Program как статический. Поэтому ссылка на метод выглядит следующим образом:

Program::hello

И тут также важно, что ссылка на метод из ссылки (hello) должен соответствовать единственному методу функционального интерфейса по типами параметров и типу результат. Так, в нашем случае метод print в интерфейсе Message имеет тип void и не принимает никаких параметров. Соответственно и статический метод hello, ссылка на который присваивается переменной интерфейса, также не принимает параметров и имеет тип void

Далее мы можем вызвать метод по ссылке:

public class Program {

    public static void main(String[] args) {
        
        Message mes;
        mes = Program::hello;

        mes.print(); // вызываем метод по ссылке
    }

    static void hello(){
        System.out.println("Hello METANIT.COM");
    }
}
interface Message{
    void print();
}

То есть при вызове

mes.print();

фактически вызывается метод по ссылке - метод hello()

Ссылка на метод предписывает компилятору создать экземпляр функционального интерфейса, переопределяя единственный метод интерфейса для его вызова. То есть в данном случае мы получаем некоторый объект интерфейса Message, где метод hello() выступает в качестве реализации метода print() из интерфейса.

Если нам надо вызвать нестатические методы, то в ссылке вместо имени класса применяется имя объекта этого класса:

public class Program {

    public static void main(String[] args) {
        
        Program prog = new Program();
        Message mes = prog::hello;

        mes.print();    // Hello METANIT.COM
    }

    void hello(){
        System.out.println("Hello METANIT.COM");
    }
}
interface Message{
    void print();
}

В данном случае метод hello() определен как нестатический. Поэтому для формирования ссылки на него создаем объект класса Program и передаем ссылку переменной mes:

Program prog = new Program();
Message mes = prog::hello;

Пример метода с параметрами

Рассмотрим другой пример, когда переменная ссылается на метод с параметрами и некоторым возвращаемым результатом:

public class Program {

    public static void main(String[] args) {
        
        Operation op = Operations::add;
        System.out.println(op.execute(5, 4));       // 9

        op = Operations::sub;
        System.out.println(op.execute(5, 4));       // 1

        op = Operations::mul;
        System.out.println(op.execute(5, 4));       // 20
    }
}
interface Operation{
    int execute(int x, int y);
}
class Operations{

    static int add(int a, int b){ return a + b; }
    static int sub(int a, int b){ return a - b; }
    static int mul(int a, int b){ return a * b; }
}

Здесь мы определяем функциональный интерфейс Operation с методом execute()

interface Operation{
    int execute(int x, int y);
}

В программе в методе main() создаем переменную op, которая представляет данный интерфейс, и присваиваем ей ссылку на статический метод Operations.add:

Operation op = Operations::add;

Мы можем так сделать, поскольку типы параметров и возвращаемый тип метода Operations.add соответствуют типам параметров и результата абстрактного метода execute() из интерфейса Operation.

Затем через переменную op вызываем метод по ссылке:

System.out.println(op.execute(5, 4));

Далее присваиваем переменной ссылку на другой метод и повторно вызываем и соответственно получаем другой результат при тех же аргументах:

op = Operations::sub;
System.out.println(op.execute(5, 4));       // 1

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

Возможность определять ссылки на методы открываем нам дополнительные возможности при написании программ. Так, мы можем определить параметр, который представляет некоторое действие, и передать ему ссылку на метод. Рассмотрим на примере:

public class Program {

    public static void main(String[] args) {
         
        doOperation(10, 4, Operations::add);  // 14
        doOperation(10, 4, Operations::sub);  // 6
        doOperation(10, 4, Operations::mul);  // 40
    } 

    static void doOperation(int a, int b, Operation op)
    {
        System.out.println(op.execute(a, b));
    }
}
interface Operation{
    int execute(int x, int y);
}
class Operations{

    static int add(int a, int b){ return a + b; }
    static int sub(int a, int b){ return a - b; }
    static int mul(int a, int b){ return a * b; }
}

Здесь возможные вычисления вынесены в отдельный класс Operations и представляют статические методы. Поэтому для передачи этих методов применяются выражения типа Operations::add

doOperation(10, 4, Operations::add);  // 14

Если нам надо вызвать нестатические методы, то в ссылке вместо имени класса применяется имя объекта этого класса:

public class Program {

    public static void main(String[] args) {
         
        Operations ops = new Operations();
        doOperation(10, 4, ops::add);  // 14
        doOperation(10, 4, ops::sub);  // 6
        doOperation(10, 4, ops::mul);  // 40
    } 

    static void doOperation(int a, int b, Operation op)
    {
        System.out.println(op.execute(a, b));
    }
}
interface Operation{
    int execute(int x, int y);
}
class Operations{

    int add(int a, int b){ return a + b; }
    int sub(int a, int b){ return a - b; }
    int mul(int a, int b){ return a * b; }
}

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

Кроме того, мы можем возвращать ссылки на методы из других методов. Например:

import java.util.function.BiFunction;

public class Program {

    public static void main(String[] args) {
        
        Operation action = select(1);  // получаем ссылку на функцию Sum
        System.out.println(action.execute(8, 5)); // 13

        action = select(2);  // получаем ссылку на функцию Subtract
        System.out.println(action.execute(8, 5));// 3

        action = select(3);  // получаем ссылку на функцию Multiply
        System.out.println(action.execute(8, 5)); // 40
    }

    static Operation select(int choice){

        // возвращаем нужную функцию в зависимости от choice
        switch (choice)
        {
            case 2:
                return Program::subtract;
            case 3:
                return Program::multiply;
            default:
                return Program::sum;
        }
    }
    static int sum(int a, int b) { return a + b; }
    static int subtract(int a, int b) { return a - b; }
    static int multiply(int a, int b) { return a * b; }
}
// Operation представляет метод, который принимает два числа int и возвращает int
interface Operation {
    int execute(int a, int b);
}

Здесь метод select() в зависимости от значения параметра возвращает ссылку на один из методов: sum, subtract или multiply.

static Operation select(int choice){

    switch (choice){

        case 2:
            return Program::subtract;
        case 3:
            return Program::multiply;
        default:
            return Program::sum;
    }
}

В методе main() получаем результат метода select() в переменную:

Operation action = select(1);

То есть в данном случае переменная action фактически будет представлять ссылку Program::sum

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

Подобным образом мы можем использовать конструкторы: название_класса::new. Например:

public class Program {

    public static void main(String[] args) {
        
        UserBuilder userBuilder = User::new;
        User user = userBuilder.create("Tom");
        System.out.println(user.getName());
    }
}
interface UserBuilder{
    User create(String name);
}

class User{
    
    private String name;
    String getName(){
        return name;
    }
    
    User(String n){
        this.name=n;
    }
}

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

Аналогично выражение Person[]::new представляет создание массива объектов Person.

Помощь сайту
Юмани:
410011174743222
Номер карты:
4048415020898850
Morty Proxy This is a proxified and sanitized view of the page, visit original site.