web-dev-qa-db-ja.com

Java 8:ストリーム内で例外をスローするメソッドをどのように扱うのですか?

クラスとメソッドがあるとします。

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

今度は、次のようなストリームによって配信されたAのインスタンスごとにfooを呼び出します。

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

質問:例外を正しく処理する方法foo()によってスローされる可能性のある例外を処理しないので、コードは私のマシン上でコンパイルされません。 barthrows Exceptionは、ここでは役に立ちません。何故ですか?

142
Bastian

メソッド呼び出しを別のメソッド呼び出しにラップする必要があります。ただし、ここではスローしませんチェック済み例外RuntimeExceptionのサブクラスであるものなら何でも投げることができます。

通常のラッピング慣用句は次のようなものです。

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

(スーパータイプ例外Exceptiononly例として使われています。決して自分で捕まえようとしないでください)

それからas.forEach(this::safeFoo)と呼びます。

126
skiwi

fooを呼び出すだけで、(ラップせずに)例外をそのまま伝搬するのであれば、代わりにJavaのforループを使用することもできます(いくつかの trickery ):

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

これは、少なくとも私のJUnitテストで私がしていることです。私は自分のチェックした例外をラップする手間をかけたくありません(実際、私のテストではラップされていない元の例外をスローしたいです)。

26
avandeursen

この質問は少し古いかもしれませんが、ここでの "正しい"答えは、コードの後半で問題を隠してしまう可能性がある唯一の方法だと思うからです。たとえ少し 論争 があっても、理由によりチェック例外が存在します。

私の意見で最もエレガントな方法は、Mishaがここで示したものです 「未来」のアクションを実行することによって、Java 8ストリームのランタイム例外を集約します 。したがって、すべての作業部分を実行し、機能していない例外を1つの例外として収集することができます。そうでなければ、それらをすべてリストにまとめて後で処理することができます。

同様のアプローチが Benji Weber からも出てきます。彼は、作業部分ではなく作業部分を収集するための独自のタイプを作成することを提案しています。

実際に入力値と出力値の単純なマッピングを実現したいものによっては、例外が発生することもあります。

あなたがこれらの方法のどれも好きでないならば、(オリジナルの例外に応じて)少なくとも自身の例外を使うことを考えてください。

13
Mariano

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);
        }
    });
}
9
fbokovikov

このようにして、例外をラップしたりアンラップしたりすることができます。

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);
    }
}
7
aalku

次のいずれかを実行します。

  • チェック済み例外を伝播します。
  • それをラップして未チェックの例外を伝播する
  • 例外をキャッチして伝播を停止します。

いくつかのライブラリ あなたは簡単にそれをさせます。以下の例は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)));
7
Robert Važan

より読みやすい方法:

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()を使用しようとしています
1
Kashyap