Смущен, когда Котлин выводит тип платформы - PullRequest
3 голосов
/ 27 октября 2019

Возникновение следующего примера кода заставило меня понять, что я не совсем понимаю, когда Котлин выводит тип платформы:

inline fun <reified U, V> LiveData<U>.map(crossinline f: (U) -> V): LiveData<V> {
    val result = MediatorLiveData<V>()
    result.addSource(this) { u -> // u: U!
        if (u is U) {
            result.value = f(u)
        }
    }
    return result
}

val x = MutableLiveData<Int>()
val y = x.map { it + 1 }

x.value = 1 // ok
x.value = null // will eventually crash from doing null + 1

Я надеялся, что использование reified + crossinline предотвратит mapфункция от вызова f, когда u равно null, потому что она проверит, если null is Int. Интересно, что null пробирается мимо проверки.

Я подумал, что, должно быть, неправильно понимаю, как работает reified + crossinline, но кажется, что на самом деле речь идет о выводе типа: U представляется как выводInt!, а не Int. Например, ручное указание параметров типа map заставляет работать:

val y = x.map<Int, Int> { it + 1 }

Итак. Почему Котлин выводит тип платформы выше? (В конце концов, x имеет тип вывода MutableLiveData<Int>, а не MutableLiveData<Int!>.) Есть ли способ не сделать вывод о типе платформы, кроме как сделать вывод самостоятельно?

1 Ответ

1 голос
/ 27 октября 2019

Первый вопрос, который вы должны задать перед тем, как приблизиться к сложностям Kotlin, таким как переработанные дженерики, это: почему я могу это сделать?

val x = MutableLiveData<Int>()
x.value = null 

и ответ на , что коренится в том, что MutableLiveData написано на Java, а не на Kotlin.

Если вы написали этот класс на Kotlin:

class MyKotlinClass<T: Any> {
    lateinit var value: T
}

, а затем попытались сделать это:

val x = MyKotlinClass<Int>()
x.value = null

тогда не скомпилируется. Это потому, что вы определили тип как ненулевой Any.

Но представьте себе, что вы пишете такой универсальный класс в Java, где Any равен Object, и вы не можете определить обнуляемость ни для чегона самом деле, за исключением предоставления аннотаций:

class ExampleJavaClass<T> {
    public ExampleJavaClass(T param) {

    }

    static ExampleJavaClass getStringInstance() {
        return new ExampleJavaClass<String>(null);
    }
}

Это разрешено. Таким образом, чтобы разрешить полностью совместимое взаимодействие с Java, Котлин должен предположить, что пустые значения в порядке. К сожалению для вас, это невидимо.

Пока кто-то не создаст специфичную для Kotlin реализацию LiveData, которая ужесточит это, вам придется иметь дело с нулем, или вы можете написать его самостоятельно, например, this . Я бы не стал поощрять написание вашего собственного - это может иметь неблагоприятные последствия.

Во многих случаях, если вы владеете кодом Java, вы можете улучшить ситуацию взаимодействия, добавив в Java аннотацию для определения допускаемой обнуляемости, например @NonNull. Однако не похоже, что вы можете сделать это, используя существующие аннотации к параметрам типа.

...