Как уже обсуждалось , разработчики API не предполагают, что разработчик хочет обрабатывать nullзначения и отсутствующие значения одинаково.
Если вы все еще хотите это сделать, вы можете сделать это явно, применив последовательность
.map(Optional::ofNullable).findFirst().flatMap(Function.identity())
к ручью. Результатом будет пустой необязательный параметр в обоих случаях, если нет первого элемента или если первый элемент есть null. Итак, в вашем случае вы можете использовать
String firstString = strings.stream()
.map(Optional::ofNullable).findFirst().flatMap(Function.identity())
.orElse(null);
чтобы получить nullзначение, если первый элемент отсутствует или null.
Если вы хотите различать эти случаи, вы можете просто пропустить этот flatMapшаг:
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"));
Это не сильно отличается от вашего обновленного вопроса. Вам просто нужно заменить "no such element"на "StringWhenListIsEmpty"и "first element is null"на null. Но если вам не нравятся условные выражения, вы также можете добиться этого, например:
String firstString = strings.stream().skip(0)
.map(Optional::ofNullable).findFirst()
.orElseGet(()->Optional.of("StringWhenListIsEmpty"))
.orElse(null);
Теперь firstStringбудет, nullесли элемент существует, но есть nullи будет, "StringWhenListIsEmpty"когда элемент не существует.