Pourquoi cela jette- java.lang.NullPointerException
t-il un ?
List<String> strings = new ArrayList<>();
strings.add(null);
strings.add("test");
String firstString = strings.stream()
.findFirst() // Exception thrown here
.orElse("StringWhenListIsEmpty");
//.orElse(null); // Changing the `orElse()` to avoid ambiguity
Le premier élément strings
est null
, qui est une valeur parfaitement acceptable. De plus, findFirst()
retourne une option , ce qui est encore plus logique pour findFirst()
pouvoir gérer null
s.
EDIT: mis orElse()
à jour le pour être moins ambigu.
java
java-8
java-stream
optional
sans fin
la source
la source
String
ici, que faire si c'est une liste qui représente une colonne dans la base de données? La valeur de la première ligne de cette colonne peut êtrenull
.null
est une valeur parfaitement acceptable en Java, d'une manière générale. En particulier, c'est un élément valide pour unArrayList<String>
. Comme toute autre valeur, cependant, il y a des limites à ce qui peut être fait avec. «Ne jamais utilisernull
» n'est pas un conseil utile, car vous ne pouvez pas l'éviter.findFirst()
, vous ne voulez rien faire d'autre.Réponses:
La raison en est l'utilisation de
Optional<T>
dans le retour. Facultatif n'est pas autorisé à contenirnull
. Essentiellement, il n'offre aucun moyen de distinguer les situations «ce n'est pas là» et «c'est là, mais c'est réglénull
».C'est pourquoi la documentation interdit explicitement la situation lorsque
null
est sélectionné dansfindFirst()
:la source
Optional
. Quoi qu'il en soit, je pense que je suis à la limite des déclamations - si le langage ne le supporte pas, il ne le supporte pas.boolean
pour différencier ces deux situations serait parfaitement logique. Pour moi, il semble que l'utilisation d'Optional<T>
ici était un choix discutable.Iterable
type, vérifiehasNext()
et renvoie la valeur appropriée.findFirst
renvoie une valeur facultative vide dans le cas de POComme indiqué précédemment , les concepteurs d'API ne supposent pas que le développeur souhaite traiter les
null
valeurs et les valeurs absentes de la même manière.Si vous souhaitez toujours le faire, vous pouvez le faire explicitement en appliquant la séquence
au flux. Le résultat sera une option vide dans les deux cas, s'il n'y a pas de premier élément ou si le premier élément l'est
null
. Donc, dans votre cas, vous pouvez utiliserString firstString = strings.stream() .map(Optional::ofNullable).findFirst().flatMap(Function.identity()) .orElse(null);
pour obtenir une
null
valeur si le premier élément est absent ounull
.Si vous souhaitez faire la distinction entre ces cas, vous pouvez simplement omettre l'
flatMap
étape:Optional<String> firstString = strings.stream() .map(Optional::ofNullable).findFirst().orElse(null); System.out.println(firstString==null? "no such element": firstString.orElse("first element is null"));
Ce n'est pas très différent de votre question mise à jour. Il suffit de remplacer
"no such element"
par"StringWhenListIsEmpty"
et"first element is null"
parnull
. Mais si vous n'aimez pas les conditions, vous pouvez également y parvenir comme:String firstString = strings.stream().skip(0) .map(Optional::ofNullable).findFirst() .orElseGet(()->Optional.of("StringWhenListIsEmpty")) .orElse(null);
Maintenant, ce
firstString
seranull
si un élément existe mais estnull
et ce sera"StringWhenListIsEmpty"
quand aucun élément n'existe.la source
null
pour 1) le premier élément estnull
ou 2) aucun élément n'existe dans la liste. J'ai mis à jour la question pour supprimer l'ambiguïté.Optional
peut être attribué ànull
. Comme ilOptional
est supposé être un "type valeur", il ne doit jamais être nul. Et une option ne doit jamais être comparée à==
. Le code peut échouer dans Java 10 :) ou à chaque fois qu'un type de valeur est introduit dans Java.null
et bien que les instances ne devraient jamais être comparées par==
, la référence peut être testée pour l'null
utilisation==
car c'est la seule façon de la testernull
. Je ne vois pas comment une telle transition vers «jamaisnull
» devrait fonctionner pour le code existant, comme l'est même la valeur par défaut de toutes les variables d'instance et éléments de tableaunull
. L'extrait de code n'est certainement pas le meilleur code, mais il ne s'agit pas non plus de traiternull
s comme des valeurs actuelles.null
. Cependant, étant donné qu'un tel changement de langage hypothétique entraînerait le compilateur à émettre une erreur ici (ne pas casser le code silencieusement), je peux vivre avec le fait qu'il devrait éventuellement être adapté pour Java 10. Je suppose que l'Stream
API va look tout à fait différent alors aussi…Vous pouvez utiliser
java.util.Objects.nonNull
pour filtrer la liste avant de trouverquelque chose comme
la source
firstString
êtrenull
si le premier élémentstrings
estnull
.Optional.of
ce qui n'est pas nul sûr. Vous pourriezmap
àOptional.ofNullable
puis utiliser ,findFirst
mais vous finirez avec une option de optionLe code suivant remplace
findFirst()
parlimit(1)
et remplaceorElse()
parreduce()
:String firstString = strings. stream(). limit(1). reduce("StringWhenListIsEmpty", (first, second) -> second);
limit()
ne permet d'atteindre qu'un seul élémentreduce
. LeBinaryOperator
passé àreduce
renvoie cet élément 1 ou bien"StringWhenListIsEmpty"
si aucun élément n'atteint lereduce
.La beauté de cette solution est qu'elle
Optional
n'est pas allouée et que leBinaryOperator
lambda n'allouera rien.la source
Facultatif est censé être un type "valeur". (lisez les petits caractères en javadoc :) JVM pourrait même tout remplacer
Optional<Foo>
par justeFoo
, en supprimant tous les coûts de boxe et de déballage. Unnull
Foo signifie un videOptional<Foo>
.Il est possible d'autoriser l'option facultative avec une valeur nulle, sans ajouter un indicateur booléen - ajoutez simplement un objet sentinelle. (pourrait même utiliser
this
comme sentinelle; voir Throwable.cause)La décision selon laquelle Facultatif ne peut pas encapsuler null n'est pas basée sur le coût d'exécution. C'était un problème très controversé et vous devez creuser les listes de diffusion. La décision n'est pas convaincante pour tout le monde.
Dans tous les cas, comme Optional ne peut pas envelopper la valeur nulle, cela nous pousse dans un coin dans des cas comme
findFirst
. Ils ont dû penser que les valeurs nulles sont très rares (il a même été considéré que Stream devrait interdire les valeurs nulles), il est donc plus pratique de lever une exception sur des valeurs nulles plutôt que sur des flux vides.Une solution de contournement consiste à encadrer
null
, par exempleclass Box<T> static Box<T> of(T value){ .. } Optional<Box<String>> first = stream.map(Box::of).findFirst();
(Ils disent que la solution à chaque problème de POO est d'introduire un autre type :)
la source
Box
type. LeOptional
type lui-même peut servir cet objectif. Voir ma réponse pour un exemple.null
est une valeur valide comme une autre, pas de traitement spécial pour elle. (jusqu'à quelque temps plus tard :)