Общие расширения KProperty1 в Котлине - PullRequest
0 голосов
/ 09 октября 2018

У меня есть следующий код:

import kotlin.reflect.KProperty1


infix fun <T, R> KProperty1<T, R>.eq(value: R) {
    println(this.name + " = $value")
}

infix fun <T, R> KProperty1<T, R>.eq(value: KProperty1<T, R>) {
    println(this.name + " = " + value.name)
}

data class Person(val age: Int, val name: String, val surname: String?)

fun main() {
    Person::age eq 1  // Valid. First function
    Person::name eq "Johan"  // Valid. First function
    Person::name eq Person::name  // Valid. Second function
    Person::age eq 1.2f  // Valid, but it shouldn't be. First function
    Person::age eq Person::name  // Valid, but it shouldn't be. First function
    Person::surname eq null  // Invalid, but it should be. Second function, but it should be first
}

Я пытаюсь создать функции расширения для класса KProperty1 с обобщениями, но мои сопоставления не соответствуют, как я ожидал.main() перечисляет некоторые варианты использования этих двух функций, с 3 неожиданными результатами в конце и описанием того, что я должен ожидать.

1 Ответ

0 голосов
/ 09 октября 2018

Во-первых, обратите внимание, что KProperty1<T, out R> имеет параметр типа out -проецируемого типа R, поэтому также можно использовать экземпляр KProperty1<Foo, Bar>, где KProperty1<Foo, Baz> ожидается, где Baz является супертипом Bar, таким как Any.

Это именно то, что происходит, когда вы вызываете eq с типами, которые не совсем совпадают: болееОбщий супертип используется для правильного разрешения вызова.

Например, этот вызов:

Person::age eq 1.2f

фактически эквивалентен:

Person::age.eq<Person, Any>(1.2f)

Youможет даже преобразовать его в эту форму, автоматически применяя Заменить инфиксный вызов обычным вызовом , а затем Добавить явные аргументы типа .

Так что всякий раз, когда типыДля точного соответствия общий супертип выбран для R.В настоящее время нет правильного способа сообщить компилятору, что он не должен возвращаться к супертипу (см. этот вопрос и ответ ).

Последний вызов является побочным эффектом того, как в настоящее время работает вывод типа:кажется, что компилятор должен выбрать одну из перегрузок, прежде чем он решит, совместим ли он для обнуляемости.Если вы замените null на (null as String?), это сработает.Пожалуйста, подайте вопрос для этого на kotl.in / issue .

В общем, желательно не смешивать общие подписи с неуниверсальными из-за возможных общих замен, которые приводят кнеоднозначность.В вашем случае замена R := KProperty1<T, Foo> (т. Е. Использование eq для свойства этого типа) приведет к неоднозначности.

Также имеет отношение:

...