1. Overview
In Java, the sneaky throw concept allows us to throw any checked exception without defining it explicitly in the method signature. This allows the omission of the throws declaration, effectively imitating the characteristics of a runtime exception.
In this article, we’ll see how this is done in practice, by looking at some code examples.
2. About Sneaky Throws
Checked exceptions are part of Java, not the JVM. In the bytecode, we can throw any exception from anywhere, without restrictions.
Java 8 brought a new type inference rule that states that a throws T is inferred as RuntimeException whenever allowed. This gives the ability to implement sneaky throws without the helper method.
A problem with sneaky throws is that you probably want to catch the exceptions eventually, but the Java compiler doesn’t allow you to catch sneakily thrown checked exceptions using exception handler for their particular exception type.
3. Sneaky Throws in Action
As we already mentioned, the compiler and the Jave Runtime can see different things:
public static <E extends Throwable> void sneakyThrow(Throwable e) throws E { throw (E) e; } private static void throwsSneakyIOException() { sneakyThrow(new IOException("sneaky")); }
The compiler sees the signature with the throws T inferred to a RuntimeException type, so it allows the unchecked exception to propagate. The Java Runtime doesn’t see any type in the throws as all throws are the same a simple throw e.
This quick test demonstrates the scenario:
@Test public void whenCallSneakyMethod_thenThrowSneakyException() { try { SneakyThrows.throwsSneakyIOException(); } catch (Exception ex) { assertEquals("sneaky", ex.getMessage().toString()); } }
It’s possible to throw a checked exception using bytecode manipulation, or Thread.stop(Throwable), but it’s messy and not recommended.
4. Using Lombok Annotations
The @SneakyThrows annotation from Lombok allows you to throw checked exceptions without using the throws declaration. This comes in handy when you need to raise an exception from a method within very restrictive interfaces like Runnable.
Say we throw an exception from within a Runnable; it will only be passed to the Thread’s unhandled exception handler.
This code will throw the Exception instance, so there is no need for you to wrap it in a RuntimeException:
public class SneakyRunnable implements Runnable { @SneakyThrows(InterruptedException.class) public void run() { throw new InterruptedException(); } }
A drawback with this code is that you cannot catch a checked exception that is not declared; so, it will not compile.
Here’s the correct form for throwing a sneaky exception:
@SneakyThrows public void run() { try { throw new InterruptedException(); } catch (InterruptedException e) { e.printStackTrace(); } }
And here’s the test for this behavior:
@Test public void whenCallSneakyRunnableMethod_thenThrowException() { try { new SneakyRunnable().run(); } catch (Exception e) { assertEquals(InterruptedException.class, e.getStackTrace()); } }
5. Conclusion
As we have seen in this article, the Java compiler can be tricked to treat checked exceptions as unchecked.
As always the code is available over on GitHub.