Waarom levert dit een java.lang.NullPointerException
op?
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
Het eerste item in strings
is null
, wat een perfect acceptabele waarde is. Bovendien retourneert findFirst()
een Optioneel, wat nog logischer is voor findFirst()
om null
s te kunnen verwerken.
EDIT: de orElse()
bijgewerkt om minder dubbelzinnig te zijn.
Antwoord 1, autoriteit 100%
De reden hiervoor is het gebruik van Optional<T>
in de return. Optioneel mag geen null
bevatten. In wezen biedt het geen manier om situaties te onderscheiden “het is er niet” en “het is er, maar het is ingesteld op null
“.
Daarom de documentatie verbiedt expliciet de situatie waarin null
is geselecteerd in findFirst()
:
Gooien:
NullPointerException
– als het geselecteerde elementnull
is
Antwoord 2, autoriteit 72%
Zoals reeds besproken, gaan de API-ontwerpers er niet vanuit dat de ontwikkelaar null
waarden en afwezige waarden op dezelfde manier.
Als je dat nog steeds wilt doen, kun je dat expliciet doen door de reeks toe te passen
.map(Optional::ofNullable).findFirst().flatMap(Function.identity())
naar de stream. Het resultaat is in beide gevallen een lege optie als er geen eerste element is of als het eerste element null
is. Dus in jouw geval mag je
String firstString = strings.stream()
.map(Optional::ofNullable).findFirst().flatMap(Function.identity())
.orElse(null);
om een null
-waarde te krijgen als het eerste element afwezig is of null
.
Als u onderscheid wilt maken tussen deze gevallen, kunt u de stap flatMap
gewoon overslaan:
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"));
Dit is niet veel anders dan uw bijgewerkte vraag. Je hoeft alleen maar "no such element"
te vervangen door "StringWhenListIsEmpty"
en "first element is null"
door null
. Maar als je niet van conditionals houdt, kun je het ook bereiken als:
String firstString = strings.stream().skip(0)
.map(Optional::ofNullable).findFirst()
.orElseGet(()->Optional.of("StringWhenListIsEmpty"))
.orElse(null);
Nu, firstString
zal null
zijn als een element bestaat maar null
is en het zal "StringWhenListIsEmpty"
als er geen element bestaat.
Antwoord 3, autoriteit 38%
U kunt java.util.Objects.nonNull
gebruiken om de lijst te filteren voordat u zoekt
zoiets als
list.stream().filter(Objects::nonNull).findFirst();