1

I am new to Java, so I hope this is not trivial, but I really could not find what I am looking for.

I have a function that throws Exception:

public String foo(String s) throws MyException {
    if ("a".equals(s)){
      return s;
    } else {
      throw new MyException("Oh no!");
    }
}

When MyException is just:

class MyException extends Exception{

  String str1;

  MyException(String str2) {
    str1=str2;
  }
  public String toString(){
    return ("MyException Occurred: "+str1) ;
  }
}

Now I have another method that calls foo inside CompletableFuture:

private CompletableFuture<String> test() throws Exception{
        return CompletableFuture.supplyAsync(() -> foo("b"));
}

But foo throws exception, so there is a compilation error here since the call for foo is unhandled exception.

All I want is to throw the original(inner) exception. How can I do that?

Alon
  • 687
  • 1
  • 6
  • 10
Liza Shakury
  • 670
  • 9
  • 20
  • Mmm I wish it worked.. I tried that, it still says unhandled exception though... because the anonymous function within the CompletableFuture.supplyAsync now throws exception, but it is not declared ... – Liza Shakury Oct 10 '18 at 21:18

3 Answers3

4

There are two issues you have.

  1. You cannot throw checked exceptions inside lambda expressions, see e.g. this answer. To handle this you can either use catch block inside the lambda expression or use runtime exception.

  2. The supplyAsync(() -> foo("b")) means it will run asynchronously in another thread at some later point, e.g. when you call .get() on the result. So it doesn't make sense for the test() method to throw the exception.

František Hartman
  • 14,436
  • 2
  • 40
  • 60
  • 2
    But note that it is a specific issue of `supplyAsync` taking a `Supplier` which can’t throw checked exceptions while the `CompletableFuture` can be completed with arbitrary throwables. Had it been a `Callable` instead, no such issue existed. [This answer](https://stackoverflow.com/a/43767613/2711488) shows how to use a `Callable`. Consider also [this answer](https://stackoverflow.com/a/28961083/2711488). – Holger Oct 11 '18 at 07:02
2
  1. The method foo must not throw a checked exception, but a not-declarable RuntimeException.

    class MyException extends RuntimeException
    
  2. Creating a Future already does not execute the foo, will do that in another call. So cannot throw anything.

    private static CompletableFuture<String> test() {
        return CompletableFuture.supplyAsync(() -> foo("b"));
    }
    
  3. Completion can be waited upon by get() or get with timeout. This will pass the thrown MyException by wrapping it as cause of an ExecutionException.

    try { test().get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.getCause().printStackTrace(); }

  4. Not to forget intercepting the exception with exceptionally:

    try {
        String s = test().exceptionally(throwable -> {
           throwable.getCause().printStackTrace();
           return "xxx"; }).get();
        System.err.println("s=" + s);
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
    
Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
1

You need to serve this checked MyException exception inside the implementation of the CompletableFuture because it is "checked exception" meaning it is dervied from Exception class. Either serve it or change the MyException to be extended from RuntimeException, then you don't need to serve it (catch it).

Krzysztof Cichocki
  • 6,294
  • 1
  • 16
  • 32
  • but I don't want to serve it... I just want to throw it back so the caller to "test" method will serve it – Liza Shakury Oct 10 '18 at 21:21
  • You can check if runtine exception was thrown using CompletableFuture.get() this will throw CompletionException with cause being the original exception, just catch it and check what it was calling gatCause(). – Krzysztof Cichocki Oct 10 '18 at 21:36