[java] findFirst ()가 찾은 첫 번째 요소가 null 인 경우 NullPointerException이 발생하는 이유는 무엇입니까?

이것은 왜 던지는가 java.lang.NullPointerException?

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

첫 번째 항목 stringsIS null완벽하게 허용되는 값이다. 또한 s 를 처리 할 수 있는 것이 훨씬 더 의미 findFirst()있는 Optional을 반환합니다 .findFirst()null

편집 : orElse()덜 모호하도록 업데이트했습니다 .



답변

그 이유 Optional<T>는 반환에 사용하기 때문입니다 . 선택 사항은을 포함 할 수 없습니다 null. 기본적으로 “거기”와 “거기”로 설정되어있는 상황을 구분하는 방법을 제공하지 않습니다 null.

이것이 문서null 가에서를 선택 했을 때 상황을 명시 적으로 금지하는 이유 입니다 findFirst().

던졌습니다 :

NullPointerException -선택한 요소가 null


답변

마찬가지로 이미 논의 의 API 디자이너는 개발자가 치료를 원하는 가정하지 않는 null값과없는 값 같은 방법으로.

그래도 그렇게하려면 시퀀스를 적용하여 명시 적으로 수행 할 수 있습니다.

.map(Optional::ofNullable).findFirst().flatMap(Function.identity())

스트림에. 첫 번째 요소가 없거나 첫 번째 요소가 인 경우 결과는 두 경우 모두 빈 옵션이 null됩니다. 따라서 귀하의 경우에는

String firstString = strings.stream()
    .map(Optional::ofNullable).findFirst().flatMap(Function.identity())
    .orElse(null);

의 GET에 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"어떤 요소가 존재하지 않는 경우.


답변

java.util.Objects.nonNull찾기 전에 목록을 필터링하는 데 사용할 수 있습니다.

뭔가

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


답변

다음 코드는 findFirst()로 대체 되고 다음으로 limit(1)대체 orElse()됩니다 reduce().

String firstString = strings.
   stream().
   limit(1).
   reduce("StringWhenListIsEmpty", (first, second) -> second);

limit()하나의 요소 만에 도달 할 수 있습니다 reduce. BinaryOperator에 전달 reduce한 요소 또는 다른 그 반환 "StringWhenListIsEmpty"에 요소가 도달하지 않는 경우 reduce.

이 솔루션 OptionalBinaryOperator장점은 할당되지 않고 람다가 아무것도 할당하지 않는다는 것입니다.


답변

선택 사항은 “값”유형이어야합니다. ( javadoc 에서 작은 글씨를 읽으십시오 🙂 JVM은 모든 것을 Optional<Foo>just로 대체 Foo하여 모든 권투 및 개봉 비용을 제거 할 수 있습니다. null푸 빈을 의미한다 Optional<Foo>.

부울 플래그를 추가하지 않고 null 값으로 Optional을 허용하도록 설계 할 수 있습니다. 센티넬 객체 만 추가하면됩니다. ( this감수자로 도 사용할 수 있습니다 . Throwable.cause 참조)

Optional이 null을 래핑 할 수 없다는 결정은 런타임 비용을 기반으로하지 않습니다. 이것은 엄청나게 논쟁적인 문제 였고 메일 링리스트를 파헤쳐 야합니다. 결정은 모든 사람에게 설득력이 없습니다.

어쨌든 Optional은 null 값을 감쌀 수 없기 때문에 다음과 같은 경우에 우리를 구석으로 밀어 넣습니다. findFirst . 그들은 null 값이 매우 드물다고 추론 했어야합니다 (Stream이 null 값을 차단해야한다고 생각하기도했습니다). 따라서 빈 스트림 대신 null 값에 예외를 던지는 것이 더 편리합니다.

해결 방법은 box입니다 null. 예 :

class Box<T>
    static Box<T> of(T value){ .. }

Optional<Box<String>> first = stream.map(Box::of).findFirst();

(그들은 모든 OOP 문제에 대한 해결책은 다른 유형을 도입하는 것이라고 말합니다 🙂


답변