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

새로운 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));

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



답변

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

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

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

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


답변

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

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

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


답변

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

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

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

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

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

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

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

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

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

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

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