Conforme já discutido , os designers de API não presumem que o desenvolvedor deseja tratar nullvalores e valores ausentes da mesma maneira.
Se ainda quiser fazer isso, você pode fazê-lo explicitamente aplicando a sequência
.map(Optional::ofNullable).findFirst().flatMap(Function.identity())
para o riacho. O resultado será um opcional vazio em ambos os casos, se não houver um primeiro elemento ou se o primeiro elemento for null. Então, no seu caso, você pode usar
String firstString = strings.stream()
.map(Optional::ofNullable).findFirst().flatMap(Function.identity())
.orElse(null);
para obter um nullvalor se o primeiro elemento estiver ausente ou null.
Se você quiser distinguir entre esses casos, pode simplesmente omitir a flatMapetapa:
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"));
Isso não é muito diferente da sua pergunta atualizada. Você apenas tem que substituir "no such element"por "StringWhenListIsEmpty"e "first element is null"por null. Mas se você não gosta de condicionais, também pode obtê-lo como:
String firstString = strings.stream().skip(0)
.map(Optional::ofNullable).findFirst()
.orElseGet(()->Optional.of("StringWhenListIsEmpty"))
.orElse(null);
Agora, firstStringserá nullse um elemento existe, mas é nulle será "StringWhenListIsEmpty"quando nenhum elemento existir.