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()
}
此实现解决了所有可空性问题,因为:
- 您必须使用默认值初始化该主题,并且该主题的内部类型不能为空 (
<T: Any>
)。
- 您从主题中获得的值始终是 non-null。如果发生了可怕的事情并且值不存在(宇宙尽头),则会抛出异常。
- 您不能将
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
}
最后几个小贴士:
- 当你处理主题时,总是使用不可变类型,因为它可能会带来很多误解。想象一下当有人修改发出的列表时会发生什么。
- 最好使用方法
hide()
将 Subject
转换为 Observable
。 Observable
以这种方式生成的不能用于向 Subject
添加新项目,因此将其传递给消费者是安全的。
科特林 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()
}
此实现解决了所有可空性问题,因为:
- 您必须使用默认值初始化该主题,并且该主题的内部类型不能为空 (
<T: Any>
)。 - 您从主题中获得的值始终是 non-null。如果发生了可怕的事情并且值不存在(宇宙尽头),则会抛出异常。
- 您不能将
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
}
最后几个小贴士:
- 当你处理主题时,总是使用不可变类型,因为它可能会带来很多误解。想象一下当有人修改发出的列表时会发生什么。
- 最好使用方法
hide()
将Subject
转换为Observable
。Observable
以这种方式生成的不能用于向Subject
添加新项目,因此将其传递给消费者是安全的。