В default методе интерфейса возвращаемое значение может не соответствовать сигнатуре метода?

Тема - лямбда выражения, в контексте паттерна цепочка ответственности. Встретился в сети такой код:

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

@FunctionalInterface
interface RequestHandler {
    public abstract Request handle(Request r);

    default RequestHandler setSuccessor(RequestHandler other) {
        return (req) -> { //req -  it's request            
            System.out.println("req instance of: " +req.getClass().getName());
            return other.handle(this.handle(req));
        };        
    }
}

public class Request {
    String data;

    public String getData() {
        return data;
    }

    public Request(String data) {
        this.data = data;
    }
}

Детальнее - есть класс Request - в нём только строка, с конструктором и геттером. Есть интерфейс RequestHandler - он обработчик запросов. В нём есть абстрактный метод handle - для лямбда выражения. И есть метод setSuccessor - для более глубоких манипуляций. Вот метод setSuccessor вызывает у меня вопросы. Почему он вообще компилится? По сигнатуре, он должен возвращать RequestHandler, но в первом return (req) - я выяснил, что req - это просто Request. (Строчка кода System.out.println("req instance of: " +req.getClass().getName()); сказала что req instance of Request) Во втором return other.handle(this.handle(req)); - возвращаемое значение, как ни крути, тоже Request, a не RequestHandler! К примеру выражение RequestHandler r = other.handle(this.handle(req)); - говорит о несовместимости типов.

Суммарный вопрос: где и в какой строке, метод setSuccessor умудрился вернуть объект интерфейса RequestHandler?


Ответы (2 шт):

Автор решения: Дмитрий Никифоров

Разберитесь, что такое функциональный интерфейс. Чтобы быть RequestHandler нужно предоставлять метод с аргументом типа Request и возвращаемым значением типа Request. Это следует из сигнатуры handle. Лямбда-выражение (req) -> {...}, грубо говоря, определяет анонимный объект с таким методом. Как вы правильно заметили, параметер req имеет тип Request, и возвращаемое значение внутри фигурных скобок тоже имеет тип Request. Получается этот объект удовлетворяет требованиям RequestHandler.

→ Ссылка
Автор решения: Andrew Kachalin

Да, действительно java может возвращать "объект интерфейса", даже если в методе возврата нигде явно не создан такой "объект". "Объект интерфейса" - пишу в кавычках, потому что мы всё равно подразумеваем объект конкретного (часто анонимного) класса, которым управляет интерфейсная ссылка.

До java 8 метод мог вернуть "объект" интерфейса как-то так:

public interface Marker { //Интерфейс
   public int doSomething(int x);
}


public Marker m(){ //Метод возвращающий "объект" интерфейса
    return    new Marker(){ //создание объекта и реализация его на базе анонимного класса
        public int doSomething(int x){ //создание конкретной реализации абстрактного метода
            return 25;
        };
    };
}

После выхода java 8, чтобы создать "объект интерфейса" нам по прежнему нужно объявить какую-то реализацию метода. Но если в интерфейсе только один абстрактный это можно сделать лямбда выражением:

 public Marker m(){ //Тот же метод, c возможностями Java 8
        return x-> 25; //Этой строчкой мы задали конкретную реализацию метода doSomething
    }

Что за магия произошла x-> 25? В терминах лямбда выражения, мы задали f(x) = 25; f(x) - это конкретная реализация абстрактного метода doSomething(int x); Язык программирования понял это как "из метода m() верни мне конкретную реализацию Marker, такую что, его единственный абстрактный метод doSomething(int x) станет возвращать константу {return 25;}; То есть, по сути чтобы создать "объект функционального интерфейса" достаточно просто реализовать его абстрактный метод лямбда выражением:

Marker m = re->re*2; //Это полностью валидный код. Сказал что реализация метода doSomething(int x){return x*2}

Возвращаясь к методу:

 default RequestHandler setSuccessor(RequestHandler other) {
        return (req) -> { //req -  это Request компилятор вывел из контекста           
            System.out.println("req instance of: " +req.getClass().getName());
            return other.handle(this.handle(req));
        };        
    }

Здесь было сказано "реализация абстрактного метод handle(зависящего от параметра Request) такая, что":

handle(Request r){
 System.out.println("r instance of: " +r.getClass().getName());
                return other.handle(this.handle(r));
} 

И далее, этот метод благополучно вернул "объект интерфейса" RequestHandler с конкретной реализацией данного метода.

→ Ссылка