Исправьте общий тип для типа первого параметра

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

Я пробовал наивный подход, но он не работал:

fun <T> Tf(x: T) { } fun main(args: Array<String>) { "1".f("1") // ok "1".f(1) // should be error } 

Кажется, что компилятор просто использует Any для T. Я хочу, чтобы T был привязан к типу приемника.

Единственный способ сделать это требует сообщить компилятору, что вы хотите.

 fun <T> Tf(x: T) { } 

Чтобы использовать его, вы должны сказать Kotlin, что вы хотите, чтобы этот тип был.

 "1".f<String>("2") // Okay "1".f(2) // Okay (see voddan's answer for a good explanation) "1".f<String>(2) // Fails because 2 isn't a String "1".f<Int>(2) // Fails because "1" isn't an Int 

Когда вы вызываете fun <T> Tf(x: T) {} как "1".f(1) , компилятор ищет общий супертип String и Int , который является Any . Затем он решает, что T is Any , и не T is Any ошибок. Единственный способ повлиять на этот процесс – явно указать T : "1".f<String>(1)

Поскольку все проверки выполняются компилятором, проблема не имеет ничего общего с стиранием типа.

Ваша проблема в том, что говорить: « Джон на 3 года старше Карла, а Карл на 3 года моложе Джона » … вы все еще не знаете ни своего возраста, ни информации. Это тот тип доказательств, который вы дали компилятору, и тогда вы ожидали, что он правильно угадает. Единственная истина, которую вы можете получить от этой информации, – это то, что Джону исполнилось как минимум 3 года, а Карлу – не менее 1 дня.

И этот тип предположения аналогичен тому, как компилятор находит общие верхние границы Any . У него было два сильных литеральных типа, из которых можно было выбирать и не иметь возможности варьироваться. Как бы он решил, важнее ли Int или String , и в то же время вы сказали, что любой T с верхними границами Any? был действителен с учетом спецификации вашего типа. Таким образом, безопасный ответ заключается в том, чтобы увидеть, могут ли оба литерала соответствовать критериям T: Any? и, конечно, они это делают, у них обоих есть предки Any . Компилятор выполнил все ваши критерии, даже если вы этого не хотели.

Если бы у вас были критерии тай-брейка, это получилось бы иначе. Например, если у вас был тип возврата T и переменная типа String получающая значение, это повлияло бы на решение вывода типа. Это, например, приводит к ошибке:

 fun <T: Any> T.f2(x: T): T = x val something: String = "1".f2(1) // ERROR 

Потому что теперь тип T привязан «левой стороной» выражения, ожидающего String без каких-либо сомнений.

Существует также вероятность того, что это также может быть проблемой вывода типа, которая не предназначена, проверьте выпущенные проблемы в YouTrack или добавьте свой собственный, чтобы получить определенный ответ от команды компилятора. Я добавил запрос функции как KT-13138 для привязки определенного типа параметра, чтобы увидеть, как команда реагирует.

Вы можете исправить T к типу приемника, установив f свойство extension, которое возвращает invokable object:

 val <T> Tf: (T) -> Unit get() = { x -> } fun main(vararg args: String) { "1".f("1") // will be OK once KT-10364 is resolved "1".f(1) // error: The integer literal does not conform to the expected type String } 

К сожалению, "1".f("1") настоящее время вызывает ошибку: «Несоответствие типов: inferred type – String, но T ожидалось». Это проблема компилятора. См. KT-10364 . См. Также KT-13139 . Вы можете голосовать и / или следить за обновлениями. Пока это не исправлено, вы все равно можете сделать следующее:

 "1".f.invoke("1") /* or */ ("1".f)("1") /* or */ val f = "1".f f("1")