The following code demonstrates a case where a checked exception, ExecutionException
, is thrown by an operation and Clojure is wrapping it with RuntimeException
.
Why is Clojure doing this? Is this normal? Clojure appears to be doing something different than Java would in this case. What is the idiomatic way to handle the actual exception, in this case Exception
, that caused the failure?
user=> (def f (future (Thread/sleep 10000) (throw (Exception. "hello world"))))
#'user/f
user=> (.get f)
Exception hello world user/fn--318 (NO_SOURCE_FILE:81)
user=> (.printStackTrace *e)
java.lang.RuntimeException: java.util.concurrent.ExecutionException: java.lang.Exception: hello world
at clojure.lang.Util.runtimeException(Util.java:165)
at clojure.lang.Reflector.invokeMatchingMethod(Reflector.java:97)
at clojure.lang.Reflector.invokeNoArgInstanceMember(Reflector.java:316)
at user$eval320.invoke(NO_SOURCE_FILE:82)
at clojure.lang.Compiler.eval(Compiler.java:6465)
at clojure.lang.Compiler.eval(Compiler.java:6431)
at clojure.core$eval.invoke(core.clj:2795)
at clojure.main$repl$read_eval_print__5967.invoke(main.clj:244)
at clojure.main$repl$fn__5972.invoke(main.clj:265)
at clojure.main$repl.doInvoke(main.clj:265)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at clojure.main$repl_opt.invoke(main.clj:331)
at clojure.main$main.doInvoke(main.clj:427)
at clojure.lang.RestFn.invoke(RestFn.java:397)
at clojure.lang.Var.invoke(Var.java:397)
at clojure.lang.AFn.applyToHelper(AFn.java:159)
at clojure.lang.Var.applyTo(Var.java:518)
at clojure.main.main(main.java:37)
Caused by: java.util.concurrent.ExecutionException: java.lang.Exception: hello world
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)
at java.util.concurrent.FutureTask.get(FutureTask.java:83)
at clojure.core$future_call$reify__5684.get(core.clj:6064)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at clojure.lang.Reflector.invokeMatchingMethod(Reflector.java:92)
... 16 more
Caused by: java.lang.Exception: hello world
at user$fn__318.invoke(NO_SOURCE_FILE:81)
nil
at clojure.core$binding_conveyor_fn$fn__3713.invoke(core.clj:1817)
at clojure.lang.AFn.call(AFn.java:18)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:680)
Note that an ExcecutionException is indeed being thrown - it's just that the exception is getting caught and handled by Clojure.
Clojure will wrap certain exceptions in a RuntimeException when it handles them - I believe this is done to avoid the need to handle checked exceptions in the Clojure source code.
If you want to access the underlying exception, then (.getCause e)
should work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With