Como ya se comentó , los diseñadores de API no asumen que el desarrollador quiera tratar los nullvalores y los valores ausentes de la misma manera.
Si aún desea hacer eso, puede hacerlo explícitamente aplicando la secuencia
.map(Optional::ofNullable).findFirst().flatMap(Function.identity())
a la corriente. El resultado será un opcional vacío en ambos casos, si no hay primer elemento o si el primer elemento lo es null. Entonces, en su caso, puede usar
String firstString = strings.stream()
.map(Optional::ofNullable).findFirst().flatMap(Function.identity())
.orElse(null);
para obtener un nullvalor si el primer elemento está ausente o null.
Si desea distinguir entre estos casos, simplemente puede omitir el flatMappaso:
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"));
Esto no es muy diferente a su pregunta actualizada. Solo tienes que reemplazar "no such element"con "StringWhenListIsEmpty"y "first element is null"con null. Pero si no te gustan los condicionales, puedes lograrlo también como:
String firstString = strings.stream().skip(0)
.map(Optional::ofNullable).findFirst()
.orElseGet(()->Optional.of("StringWhenListIsEmpty"))
.orElse(null);
Ahora, firstStringserá nullsi un elemento existe pero existe nully será "StringWhenListIsEmpty"cuando no exista ningún elemento.