Почему вывод типа Kotlin не работает для функционального интерфейса?

Я работаю над веб-приложением http4k. Http4k имеет приятную функциональную схему для обработчиков и фильтров http (также известных как перехватчики).

typealias HttpHandler = (Request) -> Response

interface Filter : (HttpHandler) -> HttpHandler {...}

Я хотел написать простой фильтр, поэтому я создал функцию, которая возвращаетFilter

fun throwNotFoundResponses(): Filter {
    return { next: HttpHandler ->
        { request: Request ->
            val response = next(request)
            if (response.status == Status.NOT_FOUND) {
                throw NotFoundException()
            }
            response
        }
    }
}

// example usage
Filter.NoOp
        .then(throwNotFoundResponses())
        .then(routes(...))

Однако Котлин жалуется (номер строки изменен в соответствии с приведенным выше примером).

NotFoundThrower.kt: (2, 12): Type mismatch: inferred type is (HttpHandler /* = (Request) -> Response */) -> (Request) -> Response but Filter was expected

Почему Kotlin не может сделать вывод, что типы на самом деле идентичны?

2 ответа

Решение

Фильтр - это интерфейс, расширяющий (HttpHandler) -> HttpHandler, поэтому это его подкласс, а не суперкласс.

Может быть, легче увидеть, если у вас нет функционального синтаксиса.

open class Animal
class Kitten: Animal()

fun doSomething(): Kitten {
    // You cannot return an explicit Animal here, even though the Kitten implementation
    // has not defined any unique members or overridden anything. 
}

Ваша лямбда - это буквально (HttpHandler) -> HttpHandlerи не может рассматриваться как Фильтр точно так же, как произвольное Животное может быть применено к Котенку. Неважно, что мы не добавляли в Kitten никаких функций или ничего не отменяли. Это простое объявление означает, что это отдельный подтип, и компилятор никогда не предположит иначе.

Вы можете использовать Filter() ~ конструктор ~ перегруженная операторная функция invoke и предоставить ему функцию фильтра:

fun throwNotFoundResponses(): Filter {
    return Filter { next: HttpHandler ->
        { request: Request ->
            val response = next(request)
            if (response.status == Status.NOT_FOUND) {
                throw NotFoundException()
            }
            response
        }
    }
}

или более кратко:

fun throwNotFoundResponses(): Filter = Filter { next: HttpHandler ->
    { request: Request ->
        next(request).takeIf { it.status != Status.NOT_FOUND } 
            ?: throw NotFoundException()
    }
}
Другие вопросы по тегам