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 Exceptionbarここでは役に立たないようです。何故ですか?



回答:


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 2014年

206
私たちは、この代わりに、私たちのネイティブの例外を使用するのでは....ああJavaは、それが与えると、その後奪うしなければならない悲しいそれをだ
エーリッヒ・

1
@Stephanその答えは削除されましたが、それはまだここにあります:stackoverflow.com/a/27661562/309308
Michael Mrozek

3
私の会社ではコードレビューがすぐに失敗します。チェックされていない例外をスローすることは許可されていません。
Stelios Adamantidis、

7
@SteliosAdamantidisそのルールは信じられないほど制限的で逆効果です。すべてのAPIのすべてのメソッドが、あなたが(もちろん、あなたが)知っている必要なく、ランタイム例外のすべての種類をスローすることを許可されていることを知っていますか?チェックされた例外の概念が実装されていないため、JavaScriptを禁止しましたか?私があなたのリード開発者だったら、代わりにチェック例外を禁止します。
spi 2018年

35

あなたが望むすべてを呼び出すことである場合foo、あなたは(ラッピングなし)であるとして、例外を伝播することを好む、あなたはまた、単にJavaの使用することができますfor(一部を反復処理可能にストリームを回した後、代わりにループを詐欺):

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

これは、少なくとも、JUnitテストで行うことです。チェックされた例外をラップするという問題を回避したくありません(実際には、ラップされていない元の例外をスローするようにテストを優先しています)。


16

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

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

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

入力値と発生した出力値の間の単純なマッピングを実際に実現したいことに応じて、例外も機能する場合があります。

これらの方法が気に入らない場合は、(元の例外に応じて)少なくとも独自の例外を使用することを検討してください。


3
これは正解としてマークする必要があります。+。しかし、それは良い投稿だとは言えません。あなたはそれを非常に詳しく説明する必要があります。独自の例外はどのように役立ちますか?発生した例外は何ですか?ここにさまざまなバリアントを用意していないのはなぜですか?リファレンスにすべてのサンプルコードがあることは、ここでは禁止された投稿スタイルと見なされます。あなたのテキストはむしろコメントの束のように見えます。
Gangnus 2017

2
あなたはそれらをキャッチしたいレベルで選択することができるはずであり、それで混乱をストリーミングします。Stream APIを使用すると、最後の操作(収集など)まで例外を処理し、そこでハンドラーで処理するか、それ以外の場合はスローする必要があります。stream.map(Streams.passException(x->mightThrowException(x))).catch(e->whatToDo(e)).collect(...)。それは例外を期待し、将来のようにそれらを処理できるようにするべきです。
aalku

10

Google Guava Throwablesクラスを使用することをお勧めします

伝播 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);
        }
    });
}

4
このメソッドは廃止されました(残念ながら)。
RobertVažan2017年

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() {
    ...
  }
}

それを通り抜けるためにそれをa 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.