kotlin 在 onNext 中传递一个 mutableList 或一个不应该为 null 的 BehaviorSubject

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

科特林 1.3.72, RxJava2

我有以下代码,我试图避免使用 !!运算符,但不确定为什么它认为该值为空,我需要使用安全调用运算符。

以后一定要用!!这是不好的做法。为什么这会是 null,因为我已经声明了任何可以为 null 的类型?

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())
        }
    }
}    

===更新=====

我做了一些修改和更新。我的最后一个使用了 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)
        }
    }

还有一个问题: 将某些内容添加到 BehaviourSubject imageSubject.value?.add(photo) 中然后立即使用 onNext imageSubject.onNext(it)?

发出该值是一种好习惯吗

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?

BehaviorSubject 中的

value 可以为空,您可以检查 Java 代码,因为它里面有 @Nullable 注释。就像@skywall说的。

这就是为什么您需要在访问 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)?

默认情况下,BehaviorSubject 具有空值。因此,当您未设置任何默认值或未在 BehaviorSubject 上发出任何内容时,它将始终具有空值。

imageSubject.value returns 为空,因此不会调用 add 方法。请注意,您在调用 add 方法之前定义了安全调用 ?

所以,总而言之,两行代码不会发出任何东西。

来自您的评论

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 !!?

您可以像这样为 BehaviorSubject 定义初始值

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

但是,为 BehaviorSubject 定义默认值不会使 value 变为 non-nullable,因为它旨在接收可为 null 的对象。

所以,为了让你不关心安全的呼叫或爆炸,你可以这样做

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)
    }
}   

BehaviourSubject 可以有 null 值,因为它旨在在订阅时发出默认值(这是它内部的最新值,如果不存在则为 null)如果您不想使用这种方法PublishSubject.

至于当前类型的实现,我会做类似的事情

class SharedViewModel : ViewModel() {
    private val compositeDisposable = CompositeDisposable()
    //It is better to use singular object streams instead of list publish
    private val imageSubject = BehaviorSubject.create<Photo>()
    //It is better to use singular object livedata instead of list post. Collect it into a list in place where you subscribe to it or use approach recommended by @Franz Andel above
    private val _selectedPhotos = MutableLiveData<Photo>()
    //Use this approach instead of explicit getter function
    val selectedPhotos: LiveData<Photo>
        get() = _selectedPhotos

    init {
        imageSubject.subscribeBy {
            _selectedPhotos.postValue(it)
        }.addTo(compositeDisposable)
    }

    fun addPhotos(photo: Photo) {
        imageSubject.onNext(photo)
    }
}    

部分推荐:

//Do not do it like that with subjects - do not add something to their value directly.
        imageSubject.value?.add(photo)
        // Using the !! is bad practice and would like to avoid it
        imageSubject.onNext(imageSubject.value!!)

回答如何在没有 !! 的情况下以最佳方式处理可空类型:

//This piece of code is pointless but it has needed context.
imageSubject.value?.apply{
   imageSubject.onNext(this)
}

您可能还想使用 elvis operator:

imageSubject.onNext(imageSubject.value ?: someDefaultValue)

首先,!! 不应被视为不好的做法。它是语言的标准部分,当您发现它适合您的用例时就可以使用它。

正如我在评论部分提到的,您必须处理 BehaviorSubject 发出的项目的可空性。解决您的问题的方法之一是将 BehaviorSubject 包装到 non-nullable 版本中:

class NonNullBehaviorSubject<T : Any> constructor(defaultValue: T) { // (1)
    private val wrappedSubject: BehaviorSubject<T> = BehaviorSubject.createDefault(defaultValue)
    val value: T // (2)
        get() {
            return wrappedSubject.value ?: throw RuntimeException("Value not available!")
        }

    fun onNext(value: T) { // (3)
        wrappedSubject.onNext(value)
    }

    fun hide(): Observable<T> = wrappedSubject.hide()
}

此实现解决了所有可空性问题,因为:

  1. 您必须使用默认值初始化该主题,并且该主题的内部类型不能为空 (<T: Any>)。
  2. 您从主题中获得的值始终是 non-null。如果发生了可怕的事情并且值不存在(宇宙尽头),则会抛出异常。
  3. 您不能将 null 值放入主题中,因为方法 onNext() 需要 value 绑定到类型 T.

如何使用:

val subject = NonNullBehaviorSubject(emptyList<Photo>()) // (4)

val newValue = subject.value.toMutableList().apply { // (2)
    add(Photo("https://m.dw.com/image/18463014_101.jpg"))
}.toList()

subject.onNext(newValue) // (3)

subject.hide().subscribe { photos -> // (5)
    // do something with photos
}    

最后几个小贴士:

  1. 当你处理主题时,总是使用不可变类型,因为它可能会带来很多误解。想象一下当有人修改发出的列表时会发生什么。
  2. 最好使用方法 hide()Subject 转换为 ObservableObservable 以这种方式生成的不能用于向 Subject 添加新项目,因此将其传递给消费者是安全的。