I initialize my variable like this:-
val user: BehaviorSubject<User?> user = BehaviorSubject.create()
But I can't do this. IDE throws an error:-
user.onNext(null)
And doing this, IDE says u will never be null:-
user.filter( u -> u!=null)
I initialize my variable like this:-
val user: BehaviorSubject<User?> user = BehaviorSubject.create()
But I can't do this. IDE throws an error:-
user.onNext(null)
And doing this, IDE says u will never be null:-
user.filter( u -> u!=null)
Thank you very much for all your answers but I ultimately went with this solution:-
class UserEnvelope(val user:User?) {}
And using this in the observables.
This best suited my requirements.
I am new to Kotlin so I don't know how to use Optionals. But from what I understand, I would have to typecast it to User type everytime I need to observe the values right?
As Guenhter explained, this is not possible. However, instead of proposing the null-object pattern, I'd recommend an implementation of the Optional
type:
data class Optional<T>(val value: T?)
fun <T> T?.asOptional() = Optional(this)
This makes your intent much clearer, and you can use a destructuring declaration in your functions:
Observable.just(Optional("Test"))
.map { (text: String?) -> text?.substring(1)?.asOptional() }
.subscribe()
Using the null-object pattern here can cause more bugs than it solves.
Optional
(with methods like java.util.Optional
) is pretty short: gist.github.com/ephemient/2dec1165b7993e6a6cd7cdfa005fe277 –
Jervis If you use rxkotlin/rxjava 2.0 (I assume so) than the answer is: you can't. The reason is explained here.
This is a break of the interface. Have a look at the Observable
Interface
public interface Observer<T> {
/** ... */
void onSubscribe(@NonNull Disposable d);
/** ... */
void onNext(@NonNull T t);
/** ... */
void onError(@NonNull Throwable e);
/** ... */
void onSubscribe(@NonNull Disposable d);
/** ... */
void onNext(@NonNull T t);
/** ... */
void onError(@NonNull Throwable e);
...
The @NonNull
will be considered by the Kotlin compiler and therefore you CAN'T pass null.
Even if you could, the onNext
would immediately throw an error:
@Override
public void onNext(T t) {
if (t == null) {
onError(new NullPointerException("onNext called with null. Null values are generally not allowed in 2.x operators and sources."));
return;
}
...
}
If you really need such a thing as null
you have to fake it. e.g. by creating a static object of User
which represents your null
-element.
e.g.
data class User(val username, val password) {
companion object {
val NULL_USER = User("", "")
}
}
...
val user = BehaviorSubject.create<User>()
...
user.onNext(User.NULL_USER)
...
user.filter { it !== User.NULL_USER }
But if is somehow possible, try to avoid the null
concept and maybe think of another solution where this isn't needed.
Thank you very much for all your answers but I ultimately went with this solution:-
class UserEnvelope(val user:User?) {}
And using this in the observables.
This best suited my requirements.
I am new to Kotlin so I don't know how to use Optionals. But from what I understand, I would have to typecast it to User type everytime I need to observe the values right?
Since RxJava 2 does not support null values, there are some other acceptable solutions you can use:
Any
class instances with your subject type. For example you could create an Empty.INSTANCE
enum class which would emulate the null value and then filter by the enum class.The last one is the one I use and prefer being a variant of the previous solution and is based on specialisations. Our friends of JetBrains always emphasise that classes are very cheap in Kotlin, so this would be a quick example to distinguish logged users and not logged ones:
abstract class SessionUser
sealed class LoggedUser(val username: String, val password: String) : SessionUser()
sealed class LogoutUser : SessionUser()
private val user = BehaviorSubject.create<SessionUser>()
private val loggedUser =
user.filter { it is LoggedUser }.cast(LoggedUser::class.java)
fun login(username: String, password: String) {
user.onNext(LoggedUser(username, password))
}
fun logout() {
user.onNext(LogoutUser())
}
I've taken an approach similar to Optional<User>
and UserEnvelope
. I make a simple User
class and a ReifiedUser
class that inherits from it. The User
class has a companion object
that has a NONE instance. The BehaviorSubject
is instantiated with the User.NONE
instance. It looks something like this:
open class User {
companion object {
val NONE = User()
}
}
class ReifiedUser(
@field:JsonProperty(J.FirstName) val firstName: String,
@field:JsonProperty(J.LastName) val lastName: String
) : User()
My BehaviorSubject
is instantiated like this:
val activeUser: BehaviorSubject<User> = BehaviorSubject.createDefault(User.NONE)
And wherever I need to use activeUser
I either flatMap
it to Observable.empty()
if it's NONE or just figure out what it is and what to do in the subscriber.
I don't like mixing java Optional
with kotlin nullable because mixing map
and let
gets really confusing and ugly. This way it's very obvious what's going on.
I think it makes more sense to write a container class such as Result
. An example of that would be
data class Result<T>(value: T?, error: Throwable?)
Usage
Observable.create { observer ->
upstreamService.listen(object: UpstreamListener {
onSuccess(data: User) {
observer.onSuccess(Result(data))
}
onError(exception: Throwable) {
observer.onSuccess(Result(null, exception))
}
}
}
© 2022 - 2024 — McMap. All rights reserved.
.onNext(null)
will evaluate to.onError(new NullPointerException(...))
. – JervisAtomicReference(null)
vsAtomicReference(User)
– Mooch