How to catch a specific runtime exception using Vavr (formerly known as Javaslang) library?
Asked Answered
M

1

6

I am trying to catch a specific runtime exception (so not throwable) and just log it (log.error has a void return type). What is the simplest way to do this in vavr?

try {
    sayHello();
} catch (MyAppRuntimeException ex) {
    log.error("Error occured") // log.error returns void not Void so I couldn't find a suitable method in  Vavr library
}

I have tried

Try.run(() -> sayHello())
   .recover(MyAppRuntimeException.class, ex->log.error("Error occured: {}", ex.getMessage()))

I get:

Bad return type void cannot be converted to Void

If .recover is not the right method please suggest alternatives where I can catch my one specific Exception but not Throwable since it catches all exceptions and errors.

Monkery answered 16/10, 2017 at 16:20 Comment(0)
T
4

It's quite easy to explain. You method - it isn't stated explicitly but I assume so based on error message returns void. Since recover also must return a value - needed for further processing - compatible with the type returned from methods it wraps, it has to return Void, so:

So the code will be:

Try
   .run(this::sayHello)
   .recover(MyAppRuntimeException.class, e -> {
      log.error("Error occured: {}", e.getMessage());
      return null;
   });

Have a look at the docs.

EDIT

After discussion in comments it turned out that recover is not what is needed. This function is used to provide a recovery value, not to log a statement, hence onFailure + instanceof (instead of pattern matching) seems to do the job:

Try
   .run(this::sayHello)
   .onFailure(throwable -> {
      if (throwable instanceof MyAppRuntimeException) {
         log.error("Error occured: {}", throwable.getMessage());
      } else {
         throw new RuntimeException(throwable);
      }
   })
Thrombo answered 16/10, 2017 at 16:55 Comment(21)
I was looking for a cleaner way if not recover some other API call but I couldn't find anything in the docs and. yes sayHello() returns void. If I have to return null for every log statement I might as well just use the Java's regular try/catch since it would look more cleanerMonkery
@user1870400, a cleaner way would be to use a onFailure method. It accepts an instance of Consumer hence to need to return anything.Thrombo
onFailure method looks like it will accept throwable. I dont want to catch throwable since it will catch all Exceptions and Errors. I only want to catch MyAppRuntimeException. Please clarifyMonkery
Yes you're right. Another way to do is to use pattern matching which might be an obverkill in this simple case. Recover is not meant to log a statement but to provide a reocvery value. onFailure is what you need - you can always use instanceof.Thrombo
I think you made a point on recover method. Could you please update the answer using onFailure and instanceOf ? so I can accept the answer. It would also help me. Thanks much! IF the instance of check fails I would like exception to be thrown as normalMonkery
What would happen if throwable instanceof MyAppRuntimeException check fails?Monkery
say I have some other exception other than MyAppRuntimeException and say the check throwable instanceof MyAppRuntimeException fails would that exception is thrown? or I will catch all exceptions and errors ?Monkery
I guess it'd be even faster if you'd tried it yourself. If this check fails nothing happens, since there's not else. All exceptions will be passed to that method but and action will be executed for MyAppRuntimeException only.Thrombo
yeah but I would want as per my question is that if the check fails it should throw as normal. since I am only trying to catch one specific exception and throw everything else. In the above example you provided I can indeed log when the MyAppRuntimeException occurs but I will also remain silent if there is any other Exception. I don't want to remain silent if there is any other Exception or error. sounds like Vavr is not mature enough to handle exceptions!Monkery
Indeed vavr has some limitations however I claim it's mature enough and very popular. This is for some reasons. What are you trying to achieve? onFailure is just a consumer you can log any exceptions there. At the beginning you wanted the single one, now you complain it logs the only one. I don't get it.Thrombo
ok I am not sure what is not clear to you. But my goal is simple. I want to log when I get a specific exception that is "MyAppRuntimeException" and if there is any other RuntimeException or an Error that occurs I want them to be thrown like any other Java program. In the example you provided it will remain silent since it is catching Throwable. is that clear enough?Monkery
It looks that you're missing the basic understanding of how vavr works :( onFailure works like peek - just enables you to see what's going there inside the flow. If exception is thrown or not at later stage is an another matter.Thrombo
True I dont understand how it works! And I don't understand even now. Ok say I have MyAppRuntimeException and DivisionByZeroException. now I want to log when MyAppRuntimeException occurs and just throw if DivisionByZeroException or any other Exception occurs. so onFailure gets Invoked when there is an Exception in the program correct ?! If so, I would inspect and see what exception that is. if it is MyAppRuntimeException I log it and if it is DivisionByZeroException I just want to throw that Exception but not remain silent.Monkery
Then leave the code as is and add get (or any other method that resolves the chain) at the end - which is functional but will throw an exception if it occurs.Thrombo
@user1870400, instead of calling get you can throw exception from else branch, however you need to wrap it into RuntimeException.Thrombo
got it! will use explicitly use throw like you showed but you can see this is a big limitation of Vavr library. I don't believe Vavr good Api call for exception handling since everything is modeled around Throwable's. There are also other people facing the same issue. github.com/vavr-io/vavr/issues/2137Monkery
That may be true, however vavr also assumes (I suppose) that yon won't be throwing an exception, but processing it if it occurs.Thrombo
yes I don't want be catching and throwing exceptions this way. this is indeed a hack because of the poor interface design by Vavr in this aspect. you can take a look at that ticket and see what other people had already suggested. They want <E extends Exception> instead of <? Extends Throwable>. That makes a huge differenceMonkery
with Cyclops library I can easily do the following for my above question Try.catchExceptions(MyAppRuntimeException.class) .try(() -> sayHello()) .onFail(MyAppRuntimeException.class, log.error("Error occured: {}", e.getMessage())) Although I like Vavr than Cyclops. I think Cyclops has better exception handling than Vavr at this time.Monkery
If throwable will be checked exception, your code will rethrow it as unchecked, which is not good code styleSuckling
Why? I see that nowadays developers tend to avoid checked exceptions.Thrombo

© 2022 - 2024 — McMap. All rights reserved.