0

TL;DR で申し訳ありませんが、説明が必要なように感じます。そうしないと、誤解される可能性があります。

RuntimeException をスローすることがあると予想される (通常は外部の) コードを呼び出すメソッドがあり、InterruptedException または ExecutionException をスローできるフューチャーを使用し、返された値の順序付けられたセットを例外がスローされるまで呼び出し、スローされた例外。動作するものを書きましたが、残念なことに、コードの見た目が間違っているように感じます。私が本当に望んでいるのは、マルチキャッチをより一般的な概念にすることです。これにより、次のような非常にクリーンなコードで問題を解決できます。

public class SomeResults {
  private final Set<SomeReturnType> valuesReturned;
  private final @Nullable RuntimeException | ExecutionException | InterruptedException exception;

  public SomeResults(Set<SomeReturnType> valuesReturned, RuntimeException | ExecutionException exception {
    this.valuesReturned = valuesReturned;
    this.exception = exception;
  }

  public Set<SomeReturnType> getValuesReturned() {
    return valuesReturned;
  }

  public @Nullable  RuntimeException | ExecutionException | InterruptedException getException();
}

そして、外部コードへの呼び出しを行う方法をまとめます...

generateResults(Bar bar) {
  // Setup code
  Set<SomeReturnType> valuesReturned = new LinkedHashSet<>();
  ...
  // loop
  {
    // stuff
    ...  // exceptions in this method should throw except for this one external code call
    try {
      valuesReturned.add(externalCodeCallGetSomeReturnValue(bar))
    }
    catch( RuntimeException | ExecutionException | InterruptedException e) {
      return new MyResults(valuesReturned, e)
    }
    ...
  }
  return new MyResults(valuesReturned, (RuntimeException | ExecutionException | InterruptedException) null);
}

そしてその後

SomeResults myResults = foo.generateResults(new Bar());
if(myResults.getException() != null) {
  throw(myResults.getException);
}

など。常に例外をすぐに再スローしたいことに注意してください。これは、誰がこれらの結果を使用して何をしたいかによって異なります。私は次のようなことをするかもしれません

try {
  SomeResults myResults = foo.generateResults(new Bar());
  Foobar Foobar = new Foobar(myResults);
}
catch(Exception e) {
  // I don't want to see any exceptions from externalCodeCallGetSomeReturnValue(bar) here
  ...
}

もちろん、例外をキャッチして結果として返すのではなく、結果を生成する関数で例外をスローさせることもできます。これには 2 つの非常に大きな問題があります。これにより、例外が返されたときにセットを最新の状態にすることができます。例えば

generateResults(Bar bar, Set<SomeReturnType> orderedListForMeToWrite) throws  ExecutionException, InterruptedException
  1. 外部メソッド呼び出しを囲むコードが実行時例外をスローするとどうなりますか? 例外呼び出しが外部コードへの実際の呼び出しによるものか、それ以外のものかを区別する簡単な方法がありません! この設計を試みたときに、実際にこの問題に遭遇しました。コードは別の場所から IllegalArgumentException をスローし、私のコード処理はそれを SomeReturnType externalCodeCallGetSomeReturnValue(Bar bar) からスローされたかのように処理しました。これはコードの健全性の問題のように思えたので、このソリューションから離れました。

私が行った解決策は、例外を例外として保存することです。しかし、その型情報を失うのは嫌でした。追加のコード作業がない場合、何かがそれをスローしたい場合は、「throws Exception」を宣言する必要がありますが、これは良くありません。同様のコードの健全性の問題があります。この状況を処理する良い方法はありますか? 私がやりたいように動作させるために私が最終的にやったことは次のとおりです:

  public static class SomeResults {
    private final Set<SomeReturnType> orderedReturnValues;
    private final @Nullable Exception exception;

    AsyncEchoesResult(Set<SomeReturnType> responses) {
      this.orderedResponses = responses;
      this.exception = null;
    }

    AsyncEchoesResult(Set<SomeReturnType> responses, RuntimeException exception) {
      this.orderedResponses = responses;
      this.exception = exception;
    }

    AsyncEchoesResult(Set<SomeReturnType> responses, ExecutionException exception) {
      this.orderedResponses = responses;
      this.exception = exception;
    }

    AsyncEchoesResult(Set<SomeReturnType> responses, InterruptedException exception) {
      this.orderedResponses = responses;
      this.exception = exception;
    }

    public Set<SomeReturnType> getResponses() {
      return orderedResponses;
    }

    public @Nullable Exception getException() {
      return exception;
    }

    public void throwExceptionIfExists() throws ExecutionException, InterruptedException {
      try {
        throw (exception);
      }
      catch (RuntimeException | ExecutionException | InterruptedException e) {
        throw e;
      }
      catch (Exception e) {
        throw new RuntimeException("Unexpected exception type in SomeResults",e);
      }
    }
  }

明らかに、これはかなり醜いです。コンストラクターがそのまま嫌いな場合は、例外を受け取る単一のコンストラクターに簡単に置き換えることができますが、型チェックが throwException() のランタイム呼び出しのみに弱まります。とにかく、よりうまく機能する代替手段はありますか?私はJDK 7で使用しているため、JDK 8の回答は興味深いものですが、私が取り組んでいるものは修正されません。

4

1 に答える 1

1

Java では、変数を「これらの型の 1 つ」として宣言することは許可されていないため、そのような型セットをサポートする唯一の構造体 (その例外をスローするコード) を使用して例外をカプセル化する必要があります。

次の型定義を検討してください。

interface ReThrower {
  void reThrow() throws RuntimeException, ExecutionException, InterruptedException;
}
static class MyResult
{
  private final Set<SomeReturnType> valuesReturned;
  private final @Nullable ReThrower exception;

  public MyResult(Set<SomeReturnType> valuesReturned, ReThrower exception) {
    this.valuesReturned = valuesReturned;
    this.exception = exception;
  }

  public Set<SomeReturnType> getValuesReturned() {
    return valuesReturned;
  }

  public void reThrowException()
    throws RuntimeException, ExecutionException, InterruptedException
  {
    if(exception!=null) exception.reThrow();
  }
}

MyResult次に、次のように作成できます。

MyResult generateResults(Bar bar) {
  // Setup code
  Set<SomeReturnType> valuesReturned = new LinkedHashSet<>();
  // …
  // loop
  {
    // stuff
    // … exceptions in this method should throw except for this one external code call
    try {
      valuesReturned.add(externalCodeCallGetSomeReturnValue(bar));
    }
    catch( RuntimeException | ExecutionException | InterruptedException e) {
      // In Java 8 you would say: new MyResult(valuesReturned, ()->{ throw e });
      return new MyResult(valuesReturned, new ReThrower() {
        public void reThrow()
            throws RuntimeException, ExecutionException, InterruptedException {
          throw e;
        }
      });
    }
    //...
  }
  return new MyResult(valuesReturned, null);
}

内部クラス (または Java 8 のラムダ式) は暗黙的に例外を格納し、その暗黙的な変数には目的の「リストされた例外タイプの 1 つ」があることに注意してください。次に、例外を安全に再スローできます。

MyResult results = new MultiCatchAndStore().generateResults(new Bar());
try
{
  results.reThrowException();
} catch(RuntimeException | ExecutionException | InterruptedException ex)
{
  // handle, of course, you could also have separate catch clauses here
}
于 2014-09-27T08:08:14.643 に答える