Waarom gooit findFirst() een NullPointerException als het eerste gevonden element null is?

Waarom levert dit een java.lang.NullPointerExceptionop?

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 stringsis null, wat een perfect acceptabele waarde is. Bovendien retourneert findFirst()een Optioneel, wat nog logischer is voor findFirst()om nulls 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 nullbevatten. 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 nullis geselecteerd in findFirst():

Gooien:

NullPointerException– als het geselecteerde element null

is


Antwoord 2, autoriteit 72%

Zoals reeds besproken, gaan de API-ontwerpers er niet vanuit dat de ontwikkelaar nullwaarden 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 nullis. 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 flatMapgewoon 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, firstStringzal nullzijn als een element bestaat maar nullis en het zal "StringWhenListIsEmpty"als er geen element bestaat.


Antwoord 3, autoriteit 38%

U kunt java.util.Objects.nonNullgebruiken om de lijst te filteren voordat u zoekt

zoiets als

list.stream().filter(Objects::nonNull).findFirst();

Other episodes