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


89

이것은 왜 던지는가 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()덜 모호하도록 업데이트했습니다 .


4
널 완벽하게 허용 값이 아닙니다 ... 사용 ""대신
미셸 Lacorte

1
@MicheleLacorte String여기서 사용하고 있지만 DB의 열을 나타내는 목록이면 어떻게됩니까? 해당 열의 첫 번째 행 값은 일 수 있습니다 null.
neverendingqs 2015 년

예,하지만 자바에서 null은 허용되지 않습니다. 쿼리를 사용하여 null을 db로 설정
Michele Lacorte 2015 년

10
@MicheleLacorte null는 일반적으로 Java에서 완벽하게 허용되는 값입니다. 특히 ArrayList<String>. 그러나 다른 값과 마찬가지로이 값으로 수행 할 수있는 작업에는 제한이 있습니다. "사용하지 마십시오 null"는 피할 수없는 유용한 조언이 아닙니다.
John Bollinger 2015 년

@NathanHughes-전화를 걸 때 쯤 findFirst()에는하고 싶은 일이 없을 것 같습니다.
neverendingqs 2015 년

답변:


72

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

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

던졌습니다 :

NullPointerException -선택한 요소가 null


3
의 인스턴스 내에 전용 부울이있는 값이 존재하는지 추적하는 것은 간단합니다 Optional. 어쨌든, 나는 내가 외침에 접하고 있다고 생각합니다. 언어가 그것을 지원하지 않으면 지원하지 않습니다.
neverendingqs 2015 년

2
@neverendingqs 절대적으로, boolean이 두 상황을 구별 하기 위해 a 를 사용하는 것은 완벽한 의미가 있습니다. 나에게는 Optional<T>여기서 사용 하는 것이 의심스러운 선택 인 것 같습니다.
Sergey Kalinichenko 2015 년

1
@neverendingqs 이상적이지 않은 자신의 null을 롤링하는 것 외에 이것에 대한 멋진 대안을 생각할 수 없습니다 .
Sergey Kalinichenko 2015 년

1
나는 모든 Iterable유형 에서 반복기를 가져오고 확인 hasNext()하고 적절한 값을 반환 하는 개인 메서드를 작성했습니다 .
neverendingqs 2015 년

1
findFirstPO의 경우 빈 Optional 값을 반환하는 것이 더 합리적이라고 생각합니다
danny

47

마찬가지로 이미 논의 의 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"어떤 요소가 존재하지 않는 경우.


죄송합니다. 내 질문이 null1) 첫 번째 요소가 null있거나 2) 목록 내에 요소가 존재하지 않기 위해 반환 하고 싶었 음을 암시했을 수 있다는 것을 깨달았습니다 . 모호함을 제거하기 위해 질문을 업데이트했습니다.
neverendingqs 2015 년

1
3 코드에서,이 Optional할당 될 수있다 null. 이후 Optional에 "값 유형"있어야하는데, 그것은 널 않을 것입니다. 그리고 Optional은 ==. 코드는 Java 10 :) 또는 Java에 값 유형이 도입 될 때마다 실패 할 수 있습니다.
ZhongYu

1
@ bayou.io : 문서가 있다는 말을하지 않는 참조 값 유형이 될 수 없습니다 null하는 동안 인스턴스 에 의해 비교해서는 안 ==, 참조가 테스트 할 수 있습니다 null사용 ==그게입니다으로 그것을 테스트하는 방법 null. 내가 얼마나 이러한 전환을 볼 수 없습니다 "결코 null모든 인스턴스 변수와 배열 요소에 대해서도 기본 값으로 기존의 코드를 작동해야하는 것은 없다" null. 스 니펫은 확실히 최고의 코드는 아니지만 nulls를 현재 가치로 취급하는 작업도 아닙니다 .
Holger 2015 년

참조 존 장미 - 도이조차 널과 함께 "=="연산자와 비교 될 수있다
중위

1
이 코드는 일반 API를 사용하고 있기 때문에,이 개념이 호출 무엇 박스 가 될 수 표현 null. 그러나 이러한 가상 언어 변경으로 인해 컴파일러가 여기서 오류를 발생 시키므로 (코드를 조용히 깨뜨리지 않음) Java 10에 맞게 조정해야한다는 사실을 감안할 수 있습니다. StreamAPI는 ...뿐만 아니라 다음 상당히 다를
홀거

18

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

뭔가

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

1
내가 원하는 firstStringnull첫 번째 항목에서이 경우 strings입니다 null.
neverendingqs

2
불행히도 그것은 Optional.of널 안전하지 않은 것을 사용 합니다. 당신은 할 mapOptional.ofNullable 있고 사용할 수 findFirst있지만 당신은 선택의 옵션으로 끝날 것입니다
Mattos

14

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

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

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

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


1

선택 사항은 "값"유형이어야합니다. ( 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 문제에 대한 해결책은 다른 유형을 도입하는 것이라고 말합니다 :)


1
다른 Box유형 을 만들 필요가 없습니다 . Optional유형 자체는이 목적을 제공 할 수 있습니다. 예를 들어 내 대답 을 참조하십시오 .
Holger

@Holger-예, 그러나 이것이 Optional의 의도 된 목적이 아니기 때문에 혼란 스러울 수 있습니다. OP의 경우 null다른 것과 같이 유효한 값이며 특별한 대우는 없습니다. (나중까지 :)
ZhongYu 2015 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.