Почему вывод типа 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()
}
}