Не удается разрешить вывод типа при перегрузке расширений функций

Учитывая следующие расширения метода (JsonResult — это просто оболочка с некоторыми дополнительными полями):

fun <T, R> T.toJson(transform: (T) -> R) = JsonResult(transform(this))

fun <T, R> List<T>.toJson(transform: (T) -> R) = JsonResult(this.map(transform))

fun <T, R> Page<T>.toJson(transform: (T) -> R) = JsonResult(this.content.map(transform)

Когда я пытаюсь вызвать любой из вышеперечисленных методов либо из списка, либо из объекта, который не является списком, я получаю ошибку компилятора с сообщением Cannot choose among the following candidates without completing type inference, которое я ожидаю

import org.springframework.data.domain.Page

val person = Person()
person.toJson { } // compiler error

val people = List<Person>()
people.toJson { } // compiler error

val pageablePeople = Page<Person>
pageablePeople.toJson { } 

Интересно, есть ли другой (идиоматический?) способ объявить эти функции без изменения их имен.


person Community    schedule 29.01.2018    source источник


Ответы (1)


Причина, по которой вы получаете ошибки разрешения перегрузки, заключается в том, что получатель типа List<T> соответствует обоим, fun <T, R> T.toJson и fun <T, R> List<T>.toJson.

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

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

Пример:

fun <T, R> toJson(t: T, transform: (T) -> R) = JsonResult(transform(t))

fun <T, R> List<T>.toJson(transform: (T) -> R) = JsonResult(map(transform))

fun <T, R> Page<T>.toJson(transform: (T) -> R) = JsonResult(content.map(transform))

fun main(args: Array<String>) {
    val person = Person()
    toJson(person) {}

    val people = listOf<Person>()
    people.toJson { }

    val pageablePeople = Page<Person>()
    pageablePeople.toJson { }
}

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

class Page<T>(val content: List<T>) {
    fun <R> toJson(transform: (T) -> R) = JsonResult(content.map(transform))
}
person Kirill Rakhman    schedule 29.01.2018
comment
Спасибо Кирилл, что нашли время ответить. Я знаю о неспособности компилятора разрешить тип, но я хотел бы сказать T, где T является прямым потомком Any, а не Collection или любого другого типа. Мне нравится обходной путь передачи несвязанного объекта в качестве аргумента. Кстати, у меня нет контроля над некоторыми классами. - person ; 29.01.2018
comment
Единственная проблема, которую я вижу с fun <T, R> toJson(t: T, transform: (T) -> R) = JsonResult(transform(t)), заключается в том, что она не согласуется с другими расширениями, и вызываемые абоненты должны помнить о вызове toJson(obj) - person ; 29.01.2018
comment
Я понимаю ваши опасения. Единственная альтернатива — переименовать одну из перегрузок. - person Kirill Rakhman; 29.01.2018