Недавно в библиотеку задач GMS было внесено изменение @Nullable. Первые несколько строк декомпилированного .class выглядит так
public abstract class Task<TResult> {
public Task() {
}
public abstract boolean isComplete();
public abstract boolean isSuccessful();
public abstract boolean isCanceled();
@Nullable
public abstract TResult getResult();
Ранее мой код Kotlin скомпилирован:
if (task.isSuccessful) {
task.result.user?.getIdToken(false)?.addOnCompleteListener { taskk ->
this.emailIdTokenCompleteListener()(taskk)
}
После обновления некоторых зависимостей gms play-services-zzz
в коде появилась ошибка компиляции:
LoginActivity.kt: (148, 28): Разрешены только безопасные (?.) Или ненулевые вызовы (!!.) На обнуляемом получателе типа AuthResult?
Вопрос в том, означает ли isSuccessful() == true
, что getResult != null
? Или было бы лучше изменить тест if (task.result != null)
на if (task.result != null)
?
То, что вы видите, это то, что компилятору Kotlin не удалось выполнить интеллектуальное приведение результата к типу NonNull на основе результата isSuccessful, это может произойти при взаимодействии как с Java, так и с кодом Kotlin.
В Kotlin 1.3 в язык была добавлена реализация "Контрактов", чтобы позволить разработчикам добавлять метаданные о методе в формате, который IDE может использовать для статического анализа и вывода типа (smart-cast).
См. Раздел 1.2 "Возвращает и подразумевает" https://proandroiddev.com/kotlin-contracts-make-great-deals-with-the-compiler-f524e57f11c
Так что с контрактами было бы правильно smart-cast, если бы реализация выглядела так:
open class Task<T> {
var result: T? = null
private set
fun isSuccessful(): Boolean {
contract {
returns(true) implies (result != null)
}
return result != null
}
}
В вашем случае, однако, вы, вероятно, хотите бросить на другого оператора безопасного вызова ?
и вызвать ваш код !task.isSuccessful
с оператором Элвиса ?:
if (task.isSuccessful) {
task.result?.user?.getIdToken(false)?.addOnCompleteListener { taskk ->
this.emailIdTokenCompleteListener()(taskk)
} ?: handleFailure() // Defensively call just in case
} else {
handleFailure()
}