0

Sorry for the TL;DR, but I feel like it needs some explanation or it will be misunderstood.

I have a method that makes a call to (generally external) code which I expect to sometimes throw a RuntimeException, and uses futures which can throw InterruptedException or ExecutionException, and I want to be able to return an ordered set of returned values to from the call up until the exception was thrown, and the exception that was thrown. I wrote something that works, but unfortunately, the way the code looks makes me feel like I'm doing something wrong. What I think I really want is multi-catch to be a more generic concept that. That would allow pretty clean code to solve it, kind of like this:

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

And have a method that wraps up making the calls to the external code ...

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

And subsequently do

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

Etc. Note that I do note always want to immediately rethrow the exception - it depends on who is using these results what they will want to do with them. I might do something like

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
  ...
}

Of course, I could let the exception get thrown in the function that generates results, instead of catching the exception and returning it as a result. This has two pretty big issues: 1. Now returning the set of values is going to be awkward - I could perhaps pass in a Set to the method that needs to "return" results and it modifies that set instead of returning a set. That allows the set to be up to date when the exception is returned. Eg

generateResults(Bar bar, Set<SomeReturnType> orderedListForMeToWrite) throws  ExecutionException, InterruptedException
  1. What if code surrounding the external method call throws a runtime exception? Now I have no easy way of distinguishing if the exception call was from the actual call to the external code, or something else! I actually ran into this issue when attempting this design. The code threw IllegalArgumentException from somewhere else, and my code handling treated it as if it had been thrown from SomeReturnType externalCodeCallGetSomeReturnValue(Bar bar). This seemed like a code health issue, which is why I moved away from this solution.

The solution I went with is to just store the exception as an Exception. However, I hated losing that type information. With no additional code work, if something wanted to throw it, it will have to declare "throws Exception", which is not good, similar code health issues there. Is there a good way to handle this situation? What I ended up doing to get it to work the way I wanted it to is as follows:

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

Obviously, this is pretty ugly. If I hate the constructors as they are I can easily enough replace them with a single one that takes an Exception, but that weakening the type-checking to only the runtime call of throwException(). Anyway, are there alternatives that work better? Note that I'm using with JDK 7 so while JDK 8 answers would be interesting, that won't fix it for what I'm working on.

m24p
  • 664
  • 4
  • 12
  • I welcome suggestions on how to improve the question. A downvote with no comments, however, leaves me with no useful information. Someone was in a bad mood? Someone accidentally clicked a button? – m24p Sep 26 '14 at 21:48
  • Your multi-constructor approach doesn't work as you can't invoke any of them from a multi-catch clause. The code of a multi-catch clause treats the catched exception as having the type of the common base class. Only when re-throwing, a difference is made. – Holger Sep 26 '14 at 23:21
  • I know, that's one of the reasons why I hate this and want a better approach. My exception code calling the constructor looks like pre-multicatch days. :-( – m24p Sep 26 '14 at 23:32

1 Answers1

1

Since Java doesn’t allow declare a variable as “one of these types” you have to encapsulate the exception using the only construct which supports such a type set: a piece of code throwing that exception.

Consider the following type definitions:

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

Then you can create a MyResult like this:

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

Note that the inner class (or lambda expression in Java 8) implicitly stores the exception and that that implicit variable has the desired “one of the listed exception type”. Then, you can safely re-throw the exception:

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
}
Holger
  • 285,553
  • 42
  • 434
  • 765