Этот код котлина:

fun badKotlin(text: String?): Boolean {
    if (text == null) {
        return true
    }

    var temp = text
    if (false) {
        temp = Arrays.deepToString(arrayOf(text))
    } 

    return temp.isBlank() // <-- only safe (?.) or non null asserted (!!.) calls
}

Не компилируется с сообщением: only safe (?.) or non null asserted (!!.) calls are allowed on a nullable receiver of type String?

Но если я добавлю else:

fun badKotlin(text: String?): Boolean {
    if (text == null) {
        return true
    }

    var temp = text
    if (false) {
        temp = Arrays.deepToString(arrayOf(text))
    } else {
        temp = Arrays.deepToString(arrayOf(text))
    }

    return temp.isBlank()
}

Все скомпилировано. Итак, почему выведение типа не удалось?

Если я изменю тип температуры на var temp: String = text, он будет успешно скопирован! Более того, если мы изменим назначение temp следующим образом: temp = String.format("%s", text), оно тоже будет скомпилировано.

ОБНОВИТЬ:

Успешно скомпилировано:

fun badKotlin(text: String?): Boolean {
    if (text == null) {
        return true
    }

    var temp = text
    if (false) {
        temp = String.format("%s", text)
    } 

    return temp.isBlank() // <-- only safe (?.) or non null asserted (!!.) calls
}

И это:

fun badKotlin(text: String?): Boolean {
    if (text == null) {
        return true
    }

    var temp: String = text
    if (false) {
        temp = Arrays.deepToString(arrayOf(text))
    } 

    return temp.isBlank() // <-- only safe (?.) or non null asserted (!!.) calls
}
0
Alexey Nikitin 13 Мар 2018 в 07:10

2 ответа

Лучший ответ

Вы могли подумать, что после

if (text == null) {
    return true
}

Тип text уточняется до String вместо String?.

Но похоже, что это не так; вместо этого компилятор вставляет умное приведение, когда видит, что text используется там, где требуется String. в

var temp = text

В строке нет причин вставлять приведение, поэтому компилятор этого не делает, а тип temp - String?.

Если вы напишете

var temp: String = text

Приведение необходимо , поэтому компилятор его вставляет.

Если вы напишете

if (...) {
    temp = Arrays.deepToString(arrayOf(text))
} else {
    temp = Arrays.deepToString(arrayOf(text))
}

Компилятор видит, что что бы ни случилось, temp было присвоено значение тип платформы String!, который снова можно умно преобразовать в String. Без ветки else этого не произойдет.

РЕДАКТИРОВАТЬ:

Любопытно, если вы просто удалите if и оставите

fun badKotlin(text: String?): Boolean {
    if (text == null) {
        return true
    }

    var temp = text

    return temp.isBlank()
}

Он компилируется, и если бы мое объяснение было полным, я бы этого не ожидал. Таким образом, компилятор поддерживает информацию, необходимую для умного преобразования, но, похоже, не применяется, потому что

В частности, смарт-приведение применяется в соответствии со следующими правилами: ...

  • var локальные переменные - если переменная не изменяется между проверкой и использованием , не фиксируется в лямбде, которая ее изменяет, и не является локальным делегированным свойством;

В случае if-else назначения в двух ветвях вместе служат еще одной проверкой; только в if, в одной ветке нет.

2
Alexey Romanov 13 Мар 2018 в 09:02

Назначив text на temp, тип температуры тоже станет String?. Как это:

var temp: String? = text

Поскольку ваш if (false) никогда не будет выполнен, это не повлияет на код. else добавляет блок, который всегда выполняется (поскольку он в основном означает if (true)), и присваивает String объекту temp, который не допускает значения NULL. Поскольку во втором примере temp не допускает значения NULL, вам больше не нужно использовать операторы безопасного вызова ...

1
s1m0nw1 13 Мар 2018 в 04:15