Java 在不抛出异常类型时出现异常类型处理问题-需要更通用的multi-catch版本

Java 在不抛出异常类型时出现异常类型处理问题-需要更通用的multi-catch版本,java,exception,generics,java-7,checked-exceptions,Java,Exception,Generics,Java 7,Checked Exceptions,为TL道歉;博士,但我觉得这需要一些解释,否则会被误解 我有一个方法可以调用(通常是外部的)代码,我希望有时会抛出RuntimeException,并使用可以抛出InterruptedException或ExecutionException的futures,我希望能够从调用中返回一组有序的返回值,直到抛出异常,以及引发的异常。我写了一些有用的东西,但不幸的是,代码的外观让我觉得我做错了什么。我想我真正想要的是多捕获是一个更通用的概念。这将允许相当干净的代码来解决它,有点像这样: public c

为TL道歉;博士,但我觉得这需要一些解释,否则会被误解

我有一个方法可以调用(通常是外部的)代码,我希望有时会抛出RuntimeException,并使用可以抛出InterruptedException或ExecutionException的futures,我希望能够从调用中返回一组有序的返回值,直到抛出异常,以及引发的异常。我写了一些有用的东西,但不幸的是,代码的外观让我觉得我做错了什么。我想我真正想要的是多捕获是一个更通用的概念。这将允许相当干净的代码来解决它,有点像这样:

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();
}
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);
}
请注意,我始终希望立即重新显示异常-这取决于谁在使用这些结果以及他们希望如何处理这些结果。我可能会这样做

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
  ...
}
当然,我可以在生成结果的函数中抛出异常,而不是捕获异常并将其作为结果返回。这有两个相当大的问题: 1.现在返回一组值会很尴尬-我可能会将一个集合传递给需要“返回”结果的方法,然后它会修改该集合而不是返回集合。这允许在返回异常时设置为最新。乙二醇

generateResults(Bar bar, Set<SomeReturnType> orderedListForMeToWrite) throws  ExecutionException, InterruptedException
generateResults(条形图,Set-orderedListForMeToWrite)引发ExecutionException、InterruptedException
  • 如果外部方法调用周围的代码引发运行时异常怎么办?现在我没有简单的方法来区分异常调用是来自对外部代码的实际调用,还是其他什么!实际上,我在尝试这种设计时遇到了这个问题。代码从其他地方抛出了IllegalArgumentException,我的代码处理将其视为是从SomeReturnType externalCodeCallGetSomeReturnValue(条形)抛出的。这似乎是一个代码运行状况问题,这就是为什么我不再使用此解决方案的原因
  • 我采用的解决方案是将异常存储为异常。然而,我讨厌丢失这种类型的信息。没有额外的代码工作,如果有东西想要抛出它,它将不得不声明“抛出异常”,这是不好的,类似的代码健康问题。有没有处理这种情况的好方法? 为了让它按我希望的方式工作,我最终做了如下工作:

      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);
          }
        }
      }
    
    公共静态类SomeResults{
    私有最终集OrderedReturnValue;
    私有final@Nullable异常;
    AsyncEchosResult(设置响应){
    this.orderedResponses=响应;
    this.exception=null;
    }
    AsyncEchosResult(设置响应、运行时异常){
    this.orderedResponses=响应;
    this.exception=异常;
    }
    AsyncEchosResult(设置响应、ExecutionException异常){
    this.orderedResponses=响应;
    this.exception=异常;
    }
    AsyncEchoseResult(设置响应、中断异常){
    this.orderedResponses=响应;
    this.exception=异常;
    }
    公共集getResponses(){
    返回orderedResponses;
    }
    public@Nullable异常getException(){
    返回异常;
    }
    public void throwExceptionIfExists()抛出ExecutionException、InterruptedException{
    试一试{
    抛出(异常);
    }
    捕获(RuntimeException | ExecutionException | InterruptedException e){
    投掷e;
    }
    捕获(例外e){
    抛出新的RuntimeException(“SomeResults中的意外异常类型”,e);
    }
    }
    }
    

    显然,这很难看。如果我讨厌构造函数,我可以很容易地用一个接受异常的构造函数来替换它们,但这会削弱类型检查,使其仅限于throwException()的运行时调用。不管怎样,有没有更好的替代方案?请注意,我使用的是JDK 7,因此虽然JDK 8的答案会很有趣,但这并不能解决我正在处理的问题。

    因为Java不允许将变量声明为“这些类型之一”,所以必须使用唯一支持此类类型集的构造来封装异常:一段引发该异常的代码

    请考虑以下类型定义:

    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();
      }
    }
    
    请注意,内部类(或Java 8中的lambda表达式)隐式存储异常,并且该隐式变量具有所需的“列出的异常类型之一”。然后,您可以安全地重新引发异常:

    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
    }
    

    我欢迎就如何改进这一问题提出建议。然而,没有任何评论的否决票让我没有任何有用的信息。有人心情不好?有人意外单击了按钮?您的多构造函数方法不起作用,因为您无法从multi-catch子句调用其中任何一个。multi-catch子句的代码将捕获的异常视为具有公共基类的类型。我知道,这就是为什么我讨厌这个,并且想要一个更好的方法的原因之一。调用构造函数的异常代码看起来像是多批次前的几天:-(
    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);
    }
    
    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
    }