クラスとメソッドがあるとします。
class A {
void foo() throws Exception() {
...
}
}
今度は、次のようなストリームによって配信されたA
のインスタンスごとにfooを呼び出します。
void bar() throws Exception {
Stream<A> as = ...
as.forEach(a -> a.foo());
}
質問:例外を正しく処理する方法foo()によってスローされる可能性のある例外を処理しないので、コードは私のマシン上でコンパイルされません。 bar
のthrows Exception
は、ここでは役に立ちません。何故ですか?
メソッド呼び出しを別のメソッド呼び出しにラップする必要があります。ただし、ここではスローしませんチェック済み例外。 RuntimeException
のサブクラスであるものなら何でも投げることができます。
通常のラッピング慣用句は次のようなものです。
private void safeFoo(final A a) {
try {
a.foo();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
(スーパータイプ例外Exception
はonly例として使われています。決して自分で捕まえようとしないでください)
それからas.forEach(this::safeFoo)
と呼びます。
foo
を呼び出すだけで、(ラップせずに)例外をそのまま伝搬するのであれば、代わりにJavaのfor
ループを使用することもできます(いくつかの trickery ):
for (A a : (Iterable<A>) as::iterator) {
a.foo();
}
これは、少なくとも私のJUnitテストで私がしていることです。私は自分のチェックした例外をラップする手間をかけたくありません(実際、私のテストではラップされていない元の例外をスローしたいです)。
この質問は少し古いかもしれませんが、ここでの "正しい"答えは、コードの後半で問題を隠してしまう可能性がある唯一の方法だと思うからです。たとえ少し 論争 があっても、理由によりチェック例外が存在します。
私の意見で最もエレガントな方法は、Mishaがここで示したものです 「未来」のアクションを実行することによって、Java 8ストリームのランタイム例外を集約します 。したがって、すべての作業部分を実行し、機能していない例外を1つの例外として収集することができます。そうでなければ、それらをすべてリストにまとめて後で処理することができます。
同様のアプローチが Benji Weber からも出てきます。彼は、作業部分ではなく作業部分を収集するための独自のタイプを作成することを提案しています。
実際に入力値と出力値の単純なマッピングを実現したいものによっては、例外が発生することもあります。
あなたがこれらの方法のどれも好きでないならば、(オリジナルの例外に応じて)少なくとも自身の例外を使うことを考えてください。
Google Guava Throwablesクラスを使用することをお勧めします
propagate(---(Throwable throwable))
RuntimeExceptionまたはErrorのインスタンスである場合はそのままスロー可能な状態で伝播します。それ以外の場合は、最後の手段として、それを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);
}
}
次のいずれかを実行します。
いくつかのライブラリ あなたは簡単にそれをさせます。以下の例はmy 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() {
...
}
}
forEach()
を過ぎてそれを取得するためにそれをRuntimeException
で隠すだけです
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()
を使用しようとしています