Search code examples
kotlinrx-java2

kotlin passing a mutableList in the onNext or a BehaviorSubject which should not be null


Kotlin 1.3.72, RxJava2

I have the following code and I am trying to avoid using the !! operator, but not sure why it thinks the value is null and I need to use the safe call operator.

Later on I have to use the !! which is bad practice. Why would this be null, as I have declared anything to be nullable types?

class SharedViewModel : ViewModel() {
    private val compositeDisposable = CompositeDisposable()
    private val imageSubject = BehaviorSubject.create<MutableList<Photo>>()
    private val selectedPhotos = MutableLiveData<List<Photo>>()

    init {
        imageSubject.subscribeBy {
            selectedPhotos.value = it
        }.addTo(compositeDisposable)
    }

    fun getSelectedPhotos(): LiveData<List<Photo>> {
        return selectedPhotos
    }

    fun addPhotos(photo: Photo) {
        // Not sure why I need the safe-call operator here
        imageSubject.value?.add(photo)
        // Using the !! is bad practice and would like to avoid it
        imageSubject.onNext(imageSubject.value!!)

        // This is how I am currently handling it, but just wondering why the value would be null when it is not a nullable type?
        if(imageSubject.value != null) {
            imageSubject.onNext(imageSubject.value ?: mutableListOf())
        }
    }
}    

=== UPDATE =====

I have made some changes and updated. My final one uses a let.

fun addPhotos(photo: Photo) {
        imageSubject.value?.add(photo)

        // Original
        imageSubject.onNext(imageSubject.value!!)

        // First Attempt
        if(imageSubject.value != null) {
            imageSubject.onNext(imageSubject.value ?: mutableListOf())
        }

        // Final Attempt
        imageSubject.value?.let {
            imageSubject.onNext(it)
        }
    }

Just another question: Is it good practice to add something into a BehaviourSubject imageSubject.value?.add(photo) and then immediately emit that value using the onNext imageSubject.onNext(it)?


Solution

  • Later on I have to use the !! which is bad practice. Why would this be null, as I have declared anything to be nullable types?

    value in BehaviorSubject is nullable, you can check into the Java code as it has @Nullable annotation inside. Just like @skywall said.

    This is what making you need to define safe call like ? or bang like !! when accessing BehaviorSubject.value.

    Just another question: Is it good practice to add something into a BehaviourSubject imageSubject.value?.add(photo) and then immediately emit that value using the onNext imageSubject.onNext(it)?

    By default, BehaviorSubject has a null value. So when you don't set any default value or haven't emitted something on your BehaviorSubject, it will always has a null value.

    imageSubject.value returns null, so add method won't be called. Notice that you define the safe call ? before calling add method.

    So, in conclusion that two lines of code won't emit anything.

    From your comment

    Would creating a BehaviorSubject with an initial value solve this issue? Or is there something I can do more in my code to make this safe without the !!?

    You can define an initial value for BehaviorSubject like this

    val imageSubject: BehaviorSubject<MutableList<Photo>> =
            BehaviorSubject.createDefault(mutableListOf(photo1, photo2))
    

    Here for more information

    But, defining default value for BehaviorSubject doesn't make the value becomes non-nullable because it is designed to receive nullable object.

    So, in order for you not to care about the safe call or bang, you could do it like this

    class SharedViewModel : ViewModel() {
        private val compositeDisposable = CompositeDisposable()
        private val imageSubject = BehaviorSubject.create<MutableList<Photo>>()
        private val selectedPhotos = MutableLiveData<List<Photo>>()
        private val photos = mutableListOf<Photo>() // Add this line
    
        ...
    
        fun addPhotos(photo: Photo) {
            photos.add(photo)
            imageSubject.onNext(photos)
        }
    }