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

Я работаю над 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(...))

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

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

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

Ответы [ 2 ]

1 голос
/ 02 апреля 2020

Фильтр - это интерфейс, который расширяет (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 и не может считаться фильтром, а не произвольным Животное может быть брошено на котенка. Неважно, что мы не добавили какие-либо функции к котенку или что-то переопределили. Это просто объявление означает, что это отдельный подтип, и компилятор никогда не примет иное.

1 голос
/ 02 апреля 2020

Вы можете использовать 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()
    }
}
...