isPresent ()를 호출하지 않고 Optional.get ()이 왜 좋지만 iterator.next ()가 아닌가?


23

새로운 Java8 스트림 API를 사용하여 컬렉션에서 하나의 특정 요소를 찾을 때 다음과 같은 코드를 작성합니다.

    String theFirstString = myCollection.stream()
            .findFirst()
            .get();

여기서 IntelliJ는 isPresent ()를 먼저 확인하지 않고 get ()을 호출한다고 경고합니다.

그러나이 코드는 다음과 같습니다.

    String theFirstString = myCollection.iterator().next();

... 경고가 없습니다.

두 가지 기술 사이에 어떤 방식으로 스트리밍 접근 방식을 "위험한"것으로 만드는 중대한 차이점이 있습니까? isPresent ()를 먼저 호출하지 않고 get ()을 호출하지 않는 것이 중요합니까? 인터넷 검색, Optional <>을 사용하여 프로그래머가 부주의하게 다루는 방법에 대한 기사를 발견하고 언제든지 get ()을 호출 할 수 있다고 가정합니다.

내 코드가 버그가있는 곳에 가능한 한 가깝게 예외가 발생하는 것을 좋아합니다.이 경우 발견 할 요소가 없을 때 get ()을 호출하는 곳입니다. 쓸모없는 에세이를 작성하고 싶지 않습니다 :

    Optional<String> firstStream = myCollection.stream()
            .findFirst();
    if (!firstStream.isPresent()) {
        throw new IllegalStateException("There is no first string even though I totally expected there to be! <sarcasm>This exception is much more useful than NoSuchElementException</sarcasm>");
    }
    String theFirstString = firstStream.get();

... 알지 못하는 get () 예외를 유발할 위험이 없다면?


Karl Bielefeldt의 답변과 Hulk의 의견을 읽은 후 위의 예외 코드가 약간 어색하다는 것을 깨달았습니다.

    String errorString = "There is no first string even though I totally expected there to be! <sarcasm>This exception is much more useful than NoSuchElementException</sarcasm>";
    String firstString = myCollection.stream()
            .findFirst()
            .orElseThrow(() -> new IllegalStateException(errorString));

이것은 더 유용 해 보이며 아마도 많은 곳에서 자연스러워 질 것입니다. 나는이 모든 것을 처리하지 않고도 목록에서 하나의 요소를 선택할 수 있기를 원한다고 생각하지만, 이러한 종류의 구조에 익숙해 져야 할 수도 있습니다.


7
당신이 사용하는 경우 예외 hrowing 부분의 형태는 마지막 예는 훨씬 더 간결하게 작성 될 수 orElseThrow을 - 당신이 던져 질 수있는 예외를하려는 것을 그것은 모든 독자에게 분명 할 것이다.
헐크

답변:


12

우선, 수표가 복잡하고 시간이 많이 소요될 수 있습니다. 정적 분석이 필요하며 두 호출 사이에 약간의 코드가있을 수 있습니다.

둘째, 두 구성의 관용어 사용법은 매우 다릅니다. 나는 스칼라에서 풀 타임으로 프로그래밍하는데, OptionsJava보다 훨씬 자주 사용합니다 . 내가 사용하는 데 필요한 어디 단일 인스턴스를 불러올 수 없습니다 .get()Option. 거의 항상 Optional함수에서 from을 반환 하거나 orElse()대신 사용해야 합니다. 이들은 예외를 던지는 것보다 결 측값을 처리하는 훨씬 우수한 방법입니다.

.get()정상적인 사용에서는 거의 호출되지 않기 때문에 IDE가 .get()올바르게 사용되었는지 확인하면 복잡한 검사를 시작하는 것이 좋습니다. 대조적으로, iterator.next()이터레이터의 적절하고 보편적 인 사용법입니다.

다시 말해서, 하나는 나쁘거나 다른 하나는 문제가 아니며, 하나는 일반적인 작동 사례이며 개발자 테스트 중에 예외를 던질 가능성이 높으며 다른 하나는 거의 사용되지 않는 문제입니다. 개발자 테스트 중에 예외를 발생시키기에 충분하지 않은 경우 IDE가 경고하기를 원하는 경우입니다.


이 선택적 비즈니스에 대해 더 자세히 알아야 할 수도 있습니다. 웹 애플리케이션에서 많이하는 객체 목록을 매핑하는 좋은 방법으로 java8을 보았습니다. 그래서 당신은 Optional <> s를 어디에나 보내야합니까? 익숙해 지려면 또 다른 SE 게시물입니다! :)
TV의 Frank Frank

@ TV 's Frank 그들은 어디에나 있어야하는 것이 적으며, 포함 된 유형의 값을 변환하는 함수를 작성하고 map또는로 묶을 수 있습니다 flatMap. Optional는 대부분 null수표로 모든 것을 묶지 않아도되는 좋은 방법 입니다.
Morgen

15

Optional 클래스는 포함 된 항목이 있는지 여부를 알 수없는 경우에 사용됩니다. 프로그래머에게 정상적으로 처리 할 수있는 추가 코드 경로가 있음을 경고하기 위해 경고가 존재합니다. 아이디어는 예외가 전혀 발생하지 않도록하는 것입니다. 제시 한 유스 케이스는 설계된 용도가 아니므로 경고는 관련이 없습니다. 실제로 예외를 발생 시키려면 계속 진행하여 비활성화하십시오 (전역이 아닌이 행에 대해서만). 인스턴스가없는 경우를 인스턴스별로 처리할지 여부를 결정하면 경고가 표시됩니다.

아마도 반복자에 대해 비슷한 경고가 생성되어야합니다. 그러나이 작업은 한 번도 수행 된 적이 없으며 지금 추가하면 기존 프로젝트에 너무 많은 경고가 표시되는 것이 좋습니다.

또한 어떤 요소가 존재할지에 대한 설명을 포함하지만 나중에 디버깅에 큰 도움이되지 않기 때문에 자신의 예외를 던지는 것이 기본 예외보다 더 유용 할 수 있습니다.


6

나는 이것들에 본질적인 차이가 없다는 데 동의하지만 더 큰 결함을 지적하고 싶습니다.

두 경우 모두 작동하지 않는 메서드를 제공하는 형식이 있으며 그 전에 다른 메서드를 호출해야합니다. IDE / 컴파일러 / 등이 하나의 사례에 대해 경고하는지 여부는 단지이 사례를 지원하는지 또는 구성했는지에 따라 다릅니다.

그럼에도 불구하고 두 코드는 코드 냄새입니다. 이러한 표현은 기본 설계 결함을 나타내며 취성 코드를 생성합니다.

당신은 물론 빨리 실패하고 싶지만, 적어도 나를위한 해결책은 그것을 으 rug하고 손을 공중에 던질 수는 없습니다 (또는 스택에 예외).

컬렉션은 현재 비어 있지 않은 것으로 알려져 있지만 실제로 신뢰할 수있는 것은 유형뿐입니다.-이것이 비어 있음을 Collection<T>보장하지는 않습니다. 현재 비어 있지 않다는 것을 알고 있지만 변경된 요구 사항으로 인해 코드가 변경되고 갑자기 빈 컬렉션에 코드가 충돌 할 수 있습니다.

이제 당신은 자유롭게 뒤로 밀고 다른 사람들에게 당신이 비어 있지 않은 목록을 얻어야한다고 말할 수 있습니다. '오류'가 빠르다는 것을 알게되어 기쁘다. 그럼에도 불구하고 소프트웨어가 손상되어 코드를 변경해야합니다.

좀 더 실용적인 접근 방식과 대조해보십시오. 컬렉션을 가져 와서 비어있을 수 있으므로 Optional # map, #orElse 또는 #get을 제외한 다른 방법을 사용하여 해당 사례를 처리해야합니다.

컴파일러는 가능한 한 많은 작업을 수행하여 정적 유형 계약을 가능한 한 많이 얻을 수 Collection<T>있습니다. 코드가이 계약을 위반하지 않는 경우 (예 :이 두 가지 냄새 나는 콜 체인 중 하나를 통해) 환경 조건이 변경되기에 덜 취약합니다.

위의 시나리오에서 빈 입력 컬렉션을 생성하는 변경은 코드에 아무런 영향을 미치지 않습니다. 또 다른 유효한 입력이므로 여전히 올바르게 처리됩니다.

이것의 비용은 a) 취성 코드를 위험에 빠뜨리거나 b) 예외를 던져서 불필요하게 복잡하게 만드는 것과는 대조적으로 더 나은 디자인에 시간을 투자해야한다는 것입니다.

마지막 참고 사항 : 모든 IDE / 컴파일러가 사전 검사없이 iterator (). next () 또는 optional.get () 호출에 대해 경고하지는 않으므로 이러한 코드는 일반적으로 리뷰에 표시됩니다. 가치있는 것을 위해, 나는 그러한 코드가 검토를 통과하고 대신 깨끗한 솔루션을 요구하도록 허용하지 않을 것입니다.


나는 코드 냄새에 대해 약간 동의 할 수 있다고 생각합니다-짧은 게시물을 만들기 위해 위의 예를 만들었습니다. 더 유효한 또 다른 경우는 목록에서 필수 요소를 뽑는 것입니다. 이는 응용 프로그램 IMO에 나타나는 이상한 일이 아닙니다.
TV의 프랭크

에 딱 맞다. "목록에서 속성 p를 가진 요소를 선택하십시오"-> list.stream (). filter (p) .findFirst () .. 그리고 다시 "우리는 그것이 없다면 어떻게됩니까?" .. 매일 빵과 버터. 그것이 의무적이며 "그냥 거기"있다면-왜 당신은 처음에 다른 많은 것들에 그것을 숨기셨습니까?
Frank

4
데이터베이스에서로드 된 목록 일 수도 있습니다. 목록은 다른 함수에서 수집 된 통계 모음 일 수도 있습니다. 우리는 객체 A, B 및 C가 있다는 것을 알고 있습니다. B에 대한 모든 양을 찾고 싶습니다.
TV의 프랭크

데이터베이스에 대해 잘 모르지만 쿼리 당 하나 이상의 결과를 보장하지는 않습니다. 통계 수집에 대해서도 마찬가지입니다. 누가 항상 비어 있지 않습니까? 나는 이것 또는 그 요소가 있어야하는 이유는 간단하지만 문서에 대한 올바른 정적 타이핑을 선호하거나 토론의 통찰력을 악화시키는 것을 선호합니다.
Frank
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.