Is LiveData null safe in Kotlin? Why can Android Studio compile it?

Viewed 883

In my mind, Kotlin is null safe.

The Code A is from the project Universal Android Music Player Sample, you can see here

It seems that val rootMediaId: LiveData<String> is not null safe in Code A, a null string can be assigned to rootMediaId, I don't know why Android Studio can compile these code, could you tell me?

I think that the Code B is reputable code.

Code A

viewModel.rootMediaId.observe(this,
                Observer<String> { rootMediaId ->
                    if (rootMediaId != null) {
                        navigateToMediaItem(rootMediaId)
                    }
                })



class MainActivityViewModel(
    private val musicServiceConnection: MusicServiceConnection
) : ViewModel() {

    val rootMediaId: LiveData<String> =
        Transformations.map(musicServiceConnection.isConnected) { isConnected ->
            if (isConnected) {
                musicServiceConnection.rootMediaId
            } else {
                null
            }
        }
   ...
}

Code B

class MainActivityViewModel(
    private val musicServiceConnection: MusicServiceConnection
) : ViewModel() {

    val rootMediaId: LiveData<String?> =
        Transformations.map(musicServiceConnection.isConnected) { isConnected ->
            if (isConnected) {
                musicServiceConnection.rootMediaId
            } else {
                null
            }
        }
   ...
}
2 Answers

It's because Live Data is written in Java so Kotlin uses platform type here. You can read more about it here. In two words: Java types don't contain nullability information so Kolin cannot infer the correct type. String from Java can be String or String? in Kotlin. Android studio marks such String types as String! so you can pay attention and set appropriate type by yourself.

Andrei Tanana's answer describes the general situation. But specifically for LiveData, note that getValue() (or value in Kotlin) is nullable and so typed as T? in Kotlin. So in a sense it doesn't matter whether the result is typed as LiveData<String> or LiveData<String?>; the values you get from it are String? either way.

This question also states Observer takes T?, but this doesn't seem to be reflected in the documentation.

Related