{"id":78,"date":"2020-12-23T09:10:40","date_gmt":"2020-12-23T07:10:40","guid":{"rendered":"http:\/\/victorrentea.ro\/blog\/?p=78"},"modified":"2020-12-25T07:29:19","modified_gmt":"2020-12-25T05:29:19","slug":"hide-checked-exceptions-with-sneakythrows","status":"publish","type":"post","link":"https:\/\/victorrentea.ro\/blog\/hide-checked-exceptions-with-sneakythrows\/","title":{"rendered":"Hide Checked Exceptions with SneakyThrows"},"content":{"rendered":"<div id=\"bsf_rt_marker\"><\/div><p>Java is the only programming language in the world that has checked exceptions, which forces the caller to know about the individual exception types thrown by the called function.<\/p>\n<p>What do you do when you face a checked exception? As we will see in a later <a href=\"exception-handling-guide-for-java\">blog post<\/a>, propagating checked exceptions through your code is both leaking implementation details, annoying, and even dangerous.  That&#8217;s why in the vast majority of cases you should catch-rethrow it as a runtime exception, and let it &quot;silently&quot; terminate your current use-case.<\/p>\n<p>This article introduces a &quot;magic&quot; way to generate that code that we wrote dozens\/hundreds of times in our career. Here&#8217;s a reminder:<\/p>\n<pre><code>public static Date parseDate(String dateStr) {\n    try {\n        SimpleDateFormat format = new SimpleDateFormat(&quot;yyyy-MM-dd&quot;);\n        return format.parse(dateStr);\n    } catch (ParseException e) {\n        throw new RuntimeException(e); \/\/ this :(\n    }\n}<\/code><\/pre>\n<p>If you are sick to do this catch-rethrow manually, here&#8217;s the <code>@SneakyThrows<\/code> annotation coming from the magic realm of the <a href=\"https:\/\/projectlombok.org\/features\/SneakyThrows\">Project Lombok<\/a>.<\/p>\n<p>Let&#8217;s just annotate our method with it and simply remove the <code>catch<\/code> clause:<\/p>\n<pre><code>@SneakyThrows\npublic static Date parseDate(String dateStr) {\n    SimpleDateFormat format = new SimpleDateFormat(&quot;yyyy-MM-dd&quot;);\n    return format.parse(dateStr);\n}<\/code><\/pre>\n<p>Doing so will instruct the Lombok annotation processor to hack the bytecode generated by the javac compiler, allowing the code above to compile, altough there&#8217;s no <code>catch<\/code>, nor <code>throws<\/code> clause for the <code>ParseException<\/code>.<\/p>\n<blockquote>\n<p><strong>Warning<\/strong>: the Java IDE you use must be hacked (see <a href=\"https:\/\/projectlombok.org\/setup\/overview\">here<\/a> how).<\/p>\n<\/blockquote>\n<p>Knowing that Lombok is able to change your code <strong>as it\u2019s compiled<\/strong>, one might expect that the current body of our function will be surrounded by <\/p>\n<pre><code>try { ... } catch (Exception e) { throw new RuntimeException(e); }<\/code><\/pre>\n<p>That\u2019s a fair expectation. Indeed, the checked exception is not swallowed but thrown back out of the function.<\/p>\n<p>However, since Java8, Lombok does it in a bit unexpected way. To see what it really does, let&#8217;s decompile the <code>.class<\/code> file:<\/p>\n<pre><code>public static Date parseDate(String dateStr) {\n    try {\n        SimpleDateFormat format = new SimpleDateFormat(&quot;yyyy-MM-dd&quot;);\n        return format.parse(dateStr);\n    } catch (Throwable var6) {\n        throw var6;\n    }\n}<\/code><\/pre>\n<p>Indeed, Lombok did add a try-catch block around the body of my function, but it caught a <code>Throwable<\/code> and rethrew it without wrapping it at all!<\/p>\n<p>Something is wrong!<\/p>\n<p>That <code>Throwable<\/code> must have been declared in a <code>throws<\/code> clause on the function!<\/p>\n<p>If you copy-paste the code in a .java file, you\u2019ll quickly see that this code doesn\u2019t compile!! But how was it compiled in the first place?!<\/p>\n<p>To understand how&#8217;s that even possible, you have to learn that the distinction between checked and runtime exceptions is only enforced by the Java compiler (javac). The Java Runtime (JVM) does NOT care what kind of exception you throw &#8211; it propagates any exception down the call stack the same way. So the Lombok processor tricked javac into producing bytecode that represents code that wouldn\u2019t actually be compilable.<\/p>\n<p>Panic!\n<img decoding=\"async\" src=\"https:\/\/victorrentea.ro\/blog\/wp-content\/uploads\/2020\/12\/image-1608413581890.png\" alt=\"file\" \/><\/p>\n<p>But a minute after you calm down, you get this strange thought: if the checked exception is invisibly thrown, how would you then be able to catch it later? Let&#8217;s try:<\/p>\n<pre><code>try { \n    parseDate(&quot;2020-01-01&quot;); \n} catch (ParseException e) {...}<\/code><\/pre>\n<p>But this doesn&#8217;t compile because nothing in the <code>try<\/code> block throws a <code>ParseException<\/code>. And javac will reject that. See for yourself: <a href=\"https:\/\/github.com\/victorrentea\/exceptions-guide\/commit\/aa1ba41cb1b7694ebb087bd405b7580b572b8ab2\">commit<\/a><\/p>\n<p>So what does this mean? It means that the exceptions hidden using <code>@SneakyThrows<\/code> aren\u2019t supposed to be caught again individually. Instead, a general <code>catch (Exception)<\/code> not ~<code>catch (RuntimeException)<\/code>~ should be in place somewhere down the call stack to catch the <em>invisible checked exception<\/em>. For example if you are handling Spring REST endpoint exceptions using <code>@RestControllerAdvice<\/code>, make sure you declare to handle <code>Exception.class<\/code>. More details in an upcoming <a href=\"presenting-exceptions-to-users\">article<\/a>.<\/p>\n<p>Some of you might be disgusted at this point. Others might be super-excited. I\u2019m not here to judge but only to report the techniques that have become wide-spread in the hundreds of projects I trained or consulted for. I agree that this can be misused if careless, so judge whether to use this feature responsibly.<\/p>\n<p>Okay, okay&#8230; But why?!<\/p>\n<p>Because Java is an old language. 25 years is a long time to carry some baggage. So today Lombok is effectively hacking the language to make us write less and more focused code.<\/p>\n<h2>Conclusion<\/h2>\n<ul>\n<li>Use Lombok&#8217;s @SneakyThrows for fatal exceptions that you don&#8217;t intend to selectively catch.<\/li>\n<li>Otherwise wrap the checked exceptions in runtime exceptions that you throw instead.<\/li>\n<\/ul>\n<hr \/>\n<p><strong>Disclaimer<\/strong>: I chose on purpose parsing a date to point out a typically unrecoverable exception. You should definitely use the new Java 8 LocalDate\/LocalDateTime API that (surprise!) doesn&#8217;t throw any more checked exceptions, but runtime ones.<\/p>\n<p>And here&#8217;s a post about the topic of whether or not to use Lombok in your Java project.<\/p>\n\n<figure class=\"wp-block-embed is-type-rich is-provider-twitter wp-block-embed-twitter\"><div class=\"wp-block-embed__wrapper\">\n<blockquote class=\"twitter-tweet\" data-width=\"550\" data-dnt=\"true\"><p lang=\"en\" dir=\"ltr\">After you get tired of Java&#39;s verbosity, you generally face a decision:<br>&#8211; Become a <a href=\"https:\/\/twitter.com\/hashtag\/Scala?src=hash&amp;ref_src=twsrc%5Etfw\">#Scala<\/a> or <a href=\"https:\/\/twitter.com\/hashtag\/Clojure?src=hash&amp;ref_src=twsrc%5Etfw\">#Clojure<\/a> scientist<br>&#8211; Become a <a href=\"https:\/\/twitter.com\/hashtag\/Kotlin?src=hash&amp;ref_src=twsrc%5Etfw\">#Kotlin<\/a> hacker\/Android<br>&#8211; Start cheating <a href=\"https:\/\/twitter.com\/hashtag\/Java?src=hash&amp;ref_src=twsrc%5Etfw\">#Java<\/a> with <a href=\"https:\/\/twitter.com\/hashtag\/Lombok?src=hash&amp;ref_src=twsrc%5Etfw\">#Lombok<\/a><br><br>\ud83d\ude02<a href=\"https:\/\/twitter.com\/hashtag\/life?src=hash&amp;ref_src=twsrc%5Etfw\">#life<\/a><br>(3rd one is most common IME)<br>BTW, Lombok&#39;s <a href=\"https:\/\/twitter.com\/value?ref_src=twsrc%5Etfw\">@Value<\/a> ~=~ Java 14&#39;s &quot;record&quot; <a href=\"https:\/\/t.co\/DR3jZ5eUpB\">pic.twitter.com\/DR3jZ5eUpB<\/a><\/p>&mdash; Victor Rentea (@VictorRentea) <a href=\"https:\/\/twitter.com\/VictorRentea\/status\/1282566184672665601?ref_src=twsrc%5Etfw\">July 13, 2020<\/a><\/blockquote><script async src=\"https:\/\/platform.twitter.com\/widgets.js\" charset=\"utf-8\"><\/script>\n<\/div><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>Java is the only programming language in the world that has checked exceptions, which forces the caller to know about the individual exception types thrown by the called function. What do you do when you &hellip; <\/p>\n","protected":false},"author":1,"featured_media":141,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[6,3],"class_list":["post-78","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-practical-tips","tag-exceptions","tag-lombok"],"_links":{"self":[{"href":"https:\/\/victorrentea.ro\/blog\/wp-json\/wp\/v2\/posts\/78","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/victorrentea.ro\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/victorrentea.ro\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/victorrentea.ro\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/victorrentea.ro\/blog\/wp-json\/wp\/v2\/comments?post=78"}],"version-history":[{"count":27,"href":"https:\/\/victorrentea.ro\/blog\/wp-json\/wp\/v2\/posts\/78\/revisions"}],"predecessor-version":[{"id":166,"href":"https:\/\/victorrentea.ro\/blog\/wp-json\/wp\/v2\/posts\/78\/revisions\/166"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/victorrentea.ro\/blog\/wp-json\/wp\/v2\/media\/141"}],"wp:attachment":[{"href":"https:\/\/victorrentea.ro\/blog\/wp-json\/wp\/v2\/media?parent=78"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/victorrentea.ro\/blog\/wp-json\/wp\/v2\/categories?post=78"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/victorrentea.ro\/blog\/wp-json\/wp\/v2\/tags?post=78"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}