web-dev-qa-db-fra.com

Comment passer null à un observable avec un type nullable dans RxJava 2 et Kotlin

J'initialise ma variable comme ceci: -

 val user: BehaviorSubject<User?> user = BehaviorSubject.create()

Mais je ne peux pas faire ça. IDE génère une erreur: -

user.onNext(null)

Et, faisant cela, IDE dit que vous ne serez jamais nul:

user.filter( u -> u!=null)
6
Abhinav Nair

Merci beaucoup pour toutes vos réponses mais je suis finalement allé avec cette solution: -

class UserEnvelope (val user: utilisateur?) {}

Et en utilisant cela dans les observables.

Cela convenait le mieux à mes exigences.

Je suis nouveau à Kotlin, donc je ne sais pas utiliser Optionals. Mais de ce que je comprends, je devrais le transtyper en type d'utilisateur à chaque fois que je dois observer les valeurs non?

1
Abhinav Nair

Comme Guenhter a expliqué, cela n’est pas possible. Cependant, au lieu de proposer le modèle null-object, je recommanderais une implémentation du type Optional:

data class Optional<T>(val value: T?)
fun <T> T?.asOptional() = Optional(this)

Cela clarifie votre intention et vous pouvez utiliser une déclaration de déstructuration dans vos fonctions:

Observable.just(Optional("Test"))
  .map { (text: String?) -> text?.substring(1)?.asOptional() }
  .subscribe()

L'utilisation du modèle null-object ici peut causer plus de bogues qu'elle n'en résout.

8
nhaarman

Si vous utilisez rxkotlin/rxjava 2.0 (je suppose donc), la réponse est: vous ne pouvez pas. La raison est expliquée ici.

C'est une rupture de l'interface. Jetez un coup d'œil à l'interface Observable

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);
...

Le @NonNull sera pris en compte par le compilateur Kotlin et par conséquent, vous NE POUVEZ PAS transmettre la valeur null.

Même si vous le pouviez, la onNext lirait immédiatement une erreur:

@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;
    }
    ...
}

Si vous avez vraiment besoin d'une chose comme null, vous devez la simuler. par exemple. en créant un objet statique de User qui représente votre null- élément.

par exemple.

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 }

Mais il est possible, essayez d'éviter le concept null et pensez peut-être à une autre solution qui ne serait pas nécessaire.

5
guenhter

Étant donné que RxJava 2 ne prend pas en charge les valeurs null , vous pouvez utiliser d’autres solutions acceptables:

  • Utilisez une bibliothèque d'encapsuleurs personnalisée ou tierce d'options, comme le suggèrent certaines des réponses postées. Lorsque je me suis débarrassé de Java au profit de Kotlin, Optionals est parti dans le même paquet, car Kotlin en soi supporte la nullabilité dans le cadre de son type System. Grâce à ce changement, le code était beaucoup plus clair et je ne souhaite personnellement pas intégrer les options dans mon code tant que je peux les éviter.
  • Émettez des instances de classe Any avec votre type de sujet. Par exemple, vous pouvez créer une classe Empty.INSTANCE enum qui émule la valeur null puis filtre par la classe enum.
  • Le dernier est celui que j'utilise et préfère être une variante de la solution précédente et est basé sur des spécialisations. Nos amis de JetBrains insistent toujours sur le fait que les cours sont très peu coûteux à Kotlin. Ce serait donc un exemple rapide permettant de distinguer les utilisateurs connectés des utilisateurs non connectés:

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