Java 8 : 스트림에서 예외 발생 메소드를 사용하려면 어떻게해야합니까?


172

클래스와 메소드가 있다고 가정하십시오.

class A {
  void foo() throws Exception() {
    ...
  }
}

이제 다음과 같은 A스트림 으로 전달되는 각 인스턴스에 대해 foo를 호출 하고 싶습니다.

void bar() throws Exception {
  Stream<A> as = ...
  as.forEach(a -> a.foo());
}

질문 : 예외를 올바르게 처리하려면 어떻게해야합니까? foo ()에서 발생할 수있는 가능한 예외를 처리하지 않기 때문에 코드가 컴퓨터에서 컴파일되지 않습니다. throws Exception의는 bar여기에 쓸모가있을 것으로 보인다. 왜 그런 겁니까?



답변:


141

확인 된 예외를 발생 시키지 않는 다른 메소드로 메소드 호출을 랩핑해야합니다 . 하위 클래스 인 것을 던질 수 있습니다 RuntimeException.

일반적인 랩핑 관용구는 다음과 같습니다.

private void safeFoo(final A a) {
    try {
        a.foo();
    } catch (Exception ex) {
        throw new RuntimeException(ex);
    }
}

(슈퍼 타입 예외 Exception는 예제 로만 사용되며, 절대 스스로 잡으려고 시도하지 마십시오)

그러면 다음과 같이 호출 할 수 있습니다 as.forEach(this::safeFoo)..


1
래퍼 메소드가 되려면 정적이라고 선언합니다. 'this'에서 아무것도 사용하지 않습니다.
aalku

206
우리가 네이티브 예외를 사용하는 대신 이것을해야하는 것이 슬프다 .... 오, 자바는주고
Erich

1
@Stephan 그 답변은 삭제되었지만 여전히 여기에서 볼 수 있습니다 : stackoverflow.com/a/27661562/309308
Michael Mrozek

3
그것은 우리 회사의 코드 검토에 즉시 실패 할 것입니다 : 우리는 확인되지 않은 예외를 던질 수 없습니다.
Stelios Adamantidis

7
@SteliosAdamantidis는 그 규칙이 엄청나게 제한적이고 역효과를 낳습니다. 모든 API에서 모든 메소드가 런타임 예외를 알지 않고도 (예를 들어) 모든 예외를 throw 할 권한이 있음을 알고 있습니까? 확인 된 예외 개념이 구현되지 않았다는 자바 스크립트를 금지 했습니까? 내가 당신의 수석 개발자라면, 대신 확인 된 예외를 금지 할 것입니다.
spi

35

원하는 모든 것을 호출 foo하고 예외를 그대로 (포장하지 않고) 전파하는 것을 선호한다면 for대신 Java의 루프를 대신 사용할 수 있습니다 ( 트릭이 있는 Stream을 Iterable로 바꾼 후 ).

for (A a : (Iterable<A>) as::iterator) {
   a.foo();
}

이것은 적어도 JUnit 테스트에서 수행하는 작업으로, 확인 된 예외를 래핑하는 데 어려움을 겪고 싶지 않습니다 (실제로 래핑되지 않은 원래 예외를 던지는 테스트를 선호합니다)


16

이 질문은 약간 오래되었지만 여기서 "올바른"답은 나중에 코드에서 숨겨진 문제를 일으킬 수있는 한 가지 방법이라고 생각하기 때문에. 논란 이 적더라도 확인 된 예외는 이유가 있습니다.

내 생각에 가장 우아한 방법은 Misha가 제시 한 것 입니다. "미래"에서 작업을 수행하여 Java 8 스트림에서 런타임 예외를 집계합니다 . 따라서 모든 작업 부분을 실행하고 작동하지 않는 예외를 단일 항목으로 수집 할 수 있습니다. 그렇지 않으면 목록에서 모두 수집하여 나중에 처리 할 수 ​​있습니다.

비슷한 접근 방식이 Benji Weber 에서 제공됩니다 . 그는 작업 부품이 아닌 작업 부품을 수집하기 위해 자체 유형을 만들 것을 제안합니다.

실제로 입력 값과 출력 값 사이의 간단한 매핑을 달성하려는 대상에 따라 예외가 발생할 수도 있습니다.

이러한 방법 중 어느 것도 마음에 들지 않으면 최소한 원래 예외에 따라 (원래 예외에 따라) 사용하는 것이 좋습니다.


3
정답으로 표시해야합니다. +. 그러나 나는 그것이 좋은 게시물이라고 말할 수 없습니다. 당신은 그것을 크게 정교하게해야합니다. 자신의 예외가 어떻게 도움이 될 수 있습니까? 발생한 예외는 무엇입니까? 왜 다른 변형을 가져 오지 않았습니까? 참조에 모든 예제 코드가있는 것은 SO에서 금지 된 포스트 스타일로 간주됩니다. 당신의 텍스트는 많은 의견으로 보입니다.
Gangnus

2
당신은 당신이 그들을 잡을 레벨을 선택할 수 있어야합니다. Stream API를 사용하면 최종 작업 (예 : 수집)까지 예외를 처리하고 처리기로 처리하거나 다르게 처리 할 수 ​​있습니다. stream.map(Streams.passException(x->mightThrowException(x))).catch(e->whatToDo(e)).collect(...). 예외를 예상하고 선물처럼 처리 할 수 ​​있습니다.
aalku

10

Google Guava Throwables 클래스를 사용하는 것이 좋습니다.

전파 ( Throwable Throwable)

RuntimeException 또는 Error의 인스턴스이거나 마지막 수단으로 Throwable을있는 그대로 전파하면 RuntimeException에 랩된 다음 전파됩니다. **

void bar() {
    Stream<A> as = ...
    as.forEach(a -> {
        try {
            a.foo()
        } catch(Exception e) {
            throw Throwables.propagate(e);
        }
    });
}

최신 정보:

더 이상 사용되지 않습니다.

void bar() {
    Stream<A> as = ...
    as.forEach(a -> {
        try {
            a.foo()
        } catch(Exception e) {
            Throwables.throwIfUnchecked(e);
            throw new RuntimeException(e);
        }
    });
}

4
이 방법은 사용되지 않습니다 (불행히도).
Robert Važan 2016 년

9

이 방법으로 예외를 랩핑하고 랩핑 해제 할 수 있습니다.

class A {
    void foo() throws Exception {
        throw new Exception();
    }
};

interface Task {
    void run() throws Exception;
}

static class TaskException extends RuntimeException {
    private static final long serialVersionUID = 1L;
    public TaskException(Exception e) {
        super(e);
    }
}

void bar() throws Exception {
      Stream<A> as = Stream.generate(()->new A());
      try {
        as.forEach(a -> wrapException(() -> a.foo())); // or a::foo instead of () -> a.foo()
    } catch (TaskException e) {
        throw (Exception)e.getCause();
    }
}

static void wrapException(Task task) {
    try {
        task.run();
    } catch (Exception e) {
        throw new TaskException(e);
    }
}

9

다음 중 하나를 수행 할 수 있습니다.

  • 전파 된 예외 확인
  • 랩핑하고 확인되지 않은 예외를 전파하거나
  • 예외를 포착하고 전파를 중지하십시오.

여러 라이브러리를 통해 쉽게 할 수 있습니다. 아래 예제는 내 NoException 라이브러리를 사용하여 작성되었습니다 .

// Propagate checked exception
as.forEach(Exceptions.sneak().consumer(A::foo));

// Wrap and propagate unchecked exception
as.forEach(Exceptions.wrap().consumer(A::foo));
as.forEach(Exceptions.wrap(MyUncheckedException::new).consumer(A::foo));

// Catch the exception and stop propagation (using logging handler for example)
as.forEach(Exceptions.log().consumer(Exceptions.sneak().consumer(A::foo)));

도서관에서 잘 했어! 나는 비슷한 것을 쓰려고했다.
Jasper de Vries

2

더 읽기 쉬운 방법 :

class A {
  void foo() throws MyException() {
    ...
  }
}

RuntimeException과거에 그것을 얻기 위해 그것을 숨기십시오forEach()

  void bar() throws MyException {
      Stream<A> as = ...
      try {
          as.forEach(a -> {
              try {
                  a.foo();
              } catch(MyException e) {
                  throw new RuntimeException(e);
              }
          });
      } catch(RuntimeException e) {
          throw (MyException) e.getCause();
      }
  }

이 시점에서 나는 스트림을 건너 뛰고 for 루프를 사용한다고 말하면 누군가를 붙들 지 않을 것입니다.

  • 을 사용하여 스트림을 만들지 않습니다 Collection.stream(). 즉 for 루프로 변환하지 않습니다.
  • 당신은 사용하려고합니다 parallelstream()
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.