클래스와 메소드가 있다고 가정하십시오.
class A {
void foo() throws Exception() {
...
}
}
이제 다음과 같은 A
스트림 으로 전달되는 각 인스턴스에 대해 foo를 호출 하고 싶습니다.
void bar() throws Exception {
Stream<A> as = ...
as.forEach(a -> a.foo());
}
질문 : 예외를 올바르게 처리하려면 어떻게해야합니까? foo ()에서 발생할 수있는 가능한 예외를 처리하지 않기 때문에 코드가 컴퓨터에서 컴파일되지 않습니다. throws Exception
의는 bar
여기에 쓸모가있을 것으로 보인다. 왜 그런 겁니까?
답변
확인 된 예외를 발생 시키지 않는 다른 메소드로 메소드 호출을 랩핑해야합니다 . 하위 클래스 인 것을 던질 수 있습니다 RuntimeException
.
일반적인 랩핑 관용구는 다음과 같습니다.
private void safeFoo(final A a) {
try {
a.foo();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
(슈퍼 타입 예외 Exception
는 예제 로만 사용되며, 절대 스스로 잡으려고 시도하지 마십시오)
그러면 다음과 같이 호출 할 수 있습니다 as.forEach(this::safeFoo)
..
답변
원하는 모든 것을 호출 foo
하고 예외를 그대로 (포장하지 않고) 전파하는 것을 선호한다면 for
대신 Java의 루프를 대신 사용할 수 있습니다 ( 트릭이 있는 Stream을 Iterable로 바꾼 후 ).
for (A a : (Iterable<A>) as::iterator) {
a.foo();
}
이것은 적어도 JUnit 테스트에서 수행하는 작업으로, 확인 된 예외를 래핑하는 데 어려움을 겪고 싶지 않습니다 (실제로 래핑되지 않은 원래 예외를 던지는 테스트를 선호합니다)
답변
이 질문은 약간 오래되었지만 여기서 “올바른”답은 나중에 코드에서 숨겨진 문제를 일으킬 수있는 한 가지 방법이라고 생각하기 때문에. 논란 이 적더라도 확인 된 예외는 이유가 있습니다.
내 생각에 가장 우아한 방법은 Misha가 제시 한 것
입니다. “미래”에서 작업을 수행하여 Java 8 스트림에서 런타임 예외를 집계합니다 . 따라서 모든 작업 부분을 실행하고 작동하지 않는 예외를 단일 항목으로 수집 할 수 있습니다. 그렇지 않으면 목록에서 모두 수집하여 나중에 처리 할 수 있습니다.
비슷한 접근 방식이 Benji Weber 에서 제공됩니다 . 그는 작업 부품이 아닌 작업 부품을 수집하기 위해 자체 유형을 만들 것을 제안합니다.
실제로 입력 값과 출력 값 사이의 간단한 매핑을 달성하려는 대상에 따라 예외가 발생할 수도 있습니다.
이러한 방법 중 어느 것도 마음에 들지 않으면 최소한 원래 예외에 따라 (원래 예외에 따라) 사용하는 것이 좋습니다.
답변
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);
}
});
}
답변
이 방법으로 예외를 랩핑하고 랩핑 해제 할 수 있습니다.
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);
}
}
답변
다음 중 하나를 수행 할 수 있습니다.
- 전파 된 예외 확인
- 랩핑하고 확인되지 않은 예외를 전파하거나
- 예외를 포착하고 전파를 중지하십시오.
여러 라이브러리를 통해 쉽게 할 수 있습니다. 아래 예제는 내 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)));
답변
더 읽기 쉬운 방법 :
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()