Я получаю ошибку неоднозначности разрешения перегрузки при безопасном вызове kotlin
У меня есть нулевая строковая переменная ab
. Если я вызываю toUpperCase
через оператор безопасного вызова после того, как я присвою ему нуль, kotlin дает ошибку.
fun main(args: Array<String>){
var ab:String? = "hello"
ab = null
println(ab?.toUpperCase())
}
Ошибка: (6, 16)
Недостаток разрешения перегрузки:
@InlineOnly public inline fun Char.toUpperCase(): Char, определенный в kotlin.text
@InlineOnly public inline fun String.toUpperCase(): Строка, определенная в kotlin.text
В чем проблема?
Ответы
Ответ 1
Как указано в этом doc о smart-casts:
x = y задает x типа y после присваивания
Линия ab = null
, вероятно, умная передача ab
до Nothing?
. Если вы проверите ab is Nothing?
, это действительно true
.
var ab: String? = "hello"
ab = null
println(ab?.toUpperCase())
println(ab is Nothing?) // true
Так как Nothing?
является подтипом всех типов (включая Char?
и String?
), это объясняет, почему вы получаете ошибку Overload resolution ambiguity
. Решением этой ошибки будет то, о чем Вилл Менцель упомянул в его ответе, выбрав ab
в тип String
перед вызовом toUpperCase()
.
Примечания:
Такая ошибка возникает, когда класс реализует два интерфейса, и оба интерфейса имеют функцию расширения одной и той же сигнатуры:
//interface
interface A {}
interface B {}
//extension function
fun A.x() = 0
fun B.x() = 0
//implementing class
class C : A, B {}
C().x() //Overload resolution ambiguity
(C() as A).x() //OK. Call A.x()
(C() as B).x() //OK. Call B.x()
Ответ 2
Я не уверен, но это похоже на ошибку из-за умного кастинга (до Nothing?
, подтипа каждого типа с нулевым именем). Это работает:
fun main(args: Array<String>) {
var ab: String? = "hello"
ab = makeNull()
println(ab?.toUpperCase())
}
fun makeNull(): String? = null
Единственное различие: компилятор не знает назначение null
напрямую, что, по-видимому, вызывает ошибку в вашем примере. Но все же, вероятно, вам тоже придется работать.
Ответ 3
Это действительно похоже на ошибку. Тип String?
как-то теряется при назначении null
, поэтому вы должны явно сказать компилятору, что он должен иметь дело с String?
.
fun main(args: Array<String>){
var ab: String? = "hello"
ab = null
println((ab as String?)?.toUpperCase()) // explicit cast
// ...or
println(ab?.let { it.toUpperCase() }) // use let
}
Ответ 4
Я считаю, что это связано с умными приведениями, используемыми Kotlin. Другими словами, Котлин может сделать вывод, что после этой строки кода:
ab = null
тип переменной ab
- это просто null
(это не фактический тип, который вы можете использовать в Kotlin - я просто ссылаюсь на диапазон допустимых значений), а не String?
(другими словами, существует no way ab
может содержать String
).
Учитывая, что функция расширения toUpperString() определена только для Char и String (а не Char? или String?), нет способа выбрать между ними.
Чтобы избежать такого поведения, см. ответы, предложенные другими ребятами (например, явное приведение в String?), но это определенно выглядит как функция (и довольно полезная), а не ошибка для меня.
Ответ 5
Я декомпилировал вашу функцию, и я подумал: после того, как вы сделаете ab = null
, компилятор будет smartcast его, помещая null
(ACONST_NULL)
в каждое возникновение ab
. Тогда как null
не имеет типа. вы не можете указать тип приемника toUpperCase()
.
Это java-эквивалентный код, сгенерированный из байтового кода kotlin:
public final void main(@NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args");
String ab = "hello";
ab = (String)null;
Object var3 = null;
System.out.println(var3);
}
Он выглядит как проблема, которая должна быть решена командой kotlin.