J'essaie de comprendre les détails de RxJava
.
Intuitivement, je m'attendais à ce que first()
et take(1)
soient égaux et fassent de même. Cependant, en creusant dans le code source, first()
est défini comme take(1).single()
.
À quoi sert la single()
ici? take(1)
ne garantit-il pas déjà la sortie d'un seul élément?
La différence est que take(1)
relaiera 0..1 éléments de l'amont tandis que first
relaiera le tout premier élément ou émettra une erreur (NoSuchElementException) si l'amont est vide. Aucun d'eux ne bloque.
C'est vrai first == take(1).single()
où take(1)
limite le nombre d'éléments en amont à 1 et single()
s'assure que l'amont n'est pas vide.
Cet exemple imprime "Terminé" uniquement
Observable.empty().take(1)
.subscribe(System.out::println, Throwable::printStackTrace,
() -> System.out.println("Done"));
Cet exemple imprime "1" suivi de "Terminé":
Observable.just(1).take(1)
.subscribe(System.out::println, Throwable::printStackTrace,
() -> System.out.println("Done"));
Cet exemple imprime également "1" suivi de "Terminé":
Observable.just(1, 2, 3).take(1)
.subscribe(System.out::println, Throwable::printStackTrace,
() -> System.out.println("Done"));
Cet exemple échoue avec NoSuchElementException
Observable.empty().first()
.subscribe(System.out::println, Throwable::printStackTrace,
() -> System.out.println("Done"));
Cet exemple, encore une fois, imprime "1" suivi de "Terminé":
Observable.just(1).first()
.subscribe(System.out::println, Throwable::printStackTrace,
() -> System.out.println("Done"));
Cet exemple, encore une fois, imprime "1" suivi de "Terminé":
Observable.just(1, 2, 3).first()
.subscribe(System.out::println, Throwable::printStackTrace,
() -> System.out.println("Done"));
Cet exemple affiche une trace de pile de NoSuchElementException
car la source contient trop peu d'éléments:
Observable.empty().single()
.subscribe(System.out::println, Throwable::printStackTrace,
() -> System.out.println("Done"));
Cet exemple affiche une trace de pile de IllegalArgumentException
car la source contient trop d'éléments:
Observable.just(1, 2, 3).single()
.subscribe(System.out::println, Throwable::printStackTrace,
() -> System.out.println("Done"));
La différence dans l'implémentation est due à la différence de sémantique. Sans aucune connaissance de la langue et de son fonctionnement interne, réfléchissez à ce que ces deux méthodes impliquent.
first()
implique qu'il retournera exactement un élément. Le premier élément de la collection.
take(x)
implique qu'il retournera une collection d'éléments. Les premiers éléments x
de la collection.
Des sémantiques différentes exigent des noms différents. Et différentes valeurs de retour nécessitent différentes implémentations techniques.
Il est également possible (encore une fois, sans regarder sous le capot pour obtenir une réponse technique), que les deux implémentations puissent gérer les conditions d'erreur très différemment. Je m'attendrais personnellement à ce que first()
lève une exception pour une collection vide, car il n'y a pas de premier élément. Cependant, je peux raisonnablement m'attendre à ce que take(x)
retourne une collection plus petite que la taille de x
, sans erreur, si la collection initiale a moins de x
éléments. Ce qui peut conduire à retourner, sans erreur, une collection vide à chaque fois qu'une collection vide est donnée.
De plus, en tant que problème technique, quelque chose comme take(x)
est plus susceptible de ne pas réellement itérer la collection sous-jacente immédiatement, mais plutôt de reporter cela jusqu'à ce que quelque chose répète son résultat. first()
, cependant, doit itérer et matérialiser le premier élément de la collection sous-jacente.