Nullability and LiveData with Kotlin

前端 未结 8 1590
梦谈多话
梦谈多话 2021-02-19 02:57

I want to use LiveData with Kotlin and have values that should not be null. How do you deal with this? Perhaps a wrapper around LiveData? Searching for good patterns here .. As

相关标签:
8条回答
  • 2021-02-19 03:04

    I really liked The Lucky Coder's answer. Regarding the danger on trying to set a null value, I think we should throw an exception for this (similar to what David Whitman was pointing):

    class NonNullLiveData<T>(private val defaultValue: T) : MutableLiveData<T>() {
    
    override fun getValue(): T = super.getValue() ?: defaultValue
    
    override fun setValue(value: T) {
        if(value != null) {
            super.setValue(value)
        }
        else throw IllegalArgumentException("Cannot set a null value to this Type. Use normal MutableLiveData instead for that.")
    }
    
    override fun postValue(value: T) {
        if(value != null) {
            super.postValue(value)
        }
        else throw IllegalArgumentException("Cannot post a null value to this Type. Use normal MutableLiveData instead for that.")
    }
    
    fun observe(owner: LifecycleOwner, body: (T) -> Unit) {
        observe(owner, Observer<T> {
            body(it ?: defaultValue)
        })
    }
    

    }

    Now the value in the MutableLiveData will never be null, so that the class would not be used in a way it's not intended.

    0 讨论(0)
  • 2021-02-19 03:14

    I don't know if this is the best solution but this is what I came up with and what I use:

    class NonNullLiveData<T>(private val defaultValue: T) : MutableLiveData<T>() {
    
        override fun getValue(): T = super.getValue() ?: defaultValue
    
        fun observe(owner: LifecycleOwner, body: (T) -> Unit) {
            observe(owner, Observer<T> {
                body(it ?: defaultValue)
            })
        }
    }
    

    Creating the field:

    val string = NonNullLiveData("")
    

    And observing it:

    viewModel.string.observe(this) {
        // Do someting with the data
    }
    
    0 讨论(0)
  • 2021-02-19 03:17

    This is my solution.

    class NotNullLiveData<T : Any>
    constructor(private val default: T) : MediatorLiveData<T>() {
        override fun getValue(): T {
            return super.getValue() ?: default
        }
    
        override fun setValue(value: T) {
            super.setValue(value)
        }
    }
    
    @MainThread
    fun <T : Any> mutableLiveDataOfNotNull(initValue: T): NotNullLiveData<T> {
        val liveData = NotNullLiveData(initValue)
        liveData.value = initValue
        return liveData
    }
    
    @MainThread
    fun <T> mutableLiveDataOf(initValue: T): MutableLiveData<T> {
        val liveData = MutableLiveData<T>()
        liveData.value = initValue
        return liveData
    }
    
    
    fun <T : Any> LiveData<T?>.toNotNull(default: T): NotNullLiveData<T> {
        val result = NotNullLiveData(default)
        result.addSource(this) {
            result.value = it ?: default
        }
        return result
    }
    
    0 讨论(0)
  • 2021-02-19 03:19

    When using strings is simple by using this way:

    val someLiveData = MutableLiveData<String>()
    ...
    someLiveData.value.orEmpty()
    

    And you will get actual value if set or an empty string instead of null.

    0 讨论(0)
  • 2021-02-19 03:20

    I created an extension property. It's not super pretty, but it is pretty straightforward.

    val <T> LiveData<T>.valueNN
        get() = this.value!!
    

    Usage

    spinner.loading = myLiveData.valueNN.homeState.loading
    

    I'm not sold on appending "NN" as a good naming convention, but that's beyond the scope of the question :)

    0 讨论(0)
  • 2021-02-19 03:22

    I little improve answer The Lucky Coder. This implementation cannot accept null values at all.

    class NonNullMutableLiveData<T: Any>(initValue: T): MutableLiveData<T>() {
    
        init {
            value = initValue
        }
    
        override fun getValue(): T {
            return super.getValue()!!
        }
    
        override fun setValue(value: T) {
            super.setValue(value)
        }
    
        fun observe(owner: LifecycleOwner, body: (T) -> Unit) {
            observe(owner, Observer<T> { t -> body(t!!) })
        }
    
        override fun postValue(value: T) {
            super.postValue(value)
        }    
    }
    
    0 讨论(0)
提交回复
热议问题