0

I am trying to understand how the @SneakyThrows annotation in Lombok actually works under the hood.

From this SO answer, I can gather it uses the method Lombok.sneakyThrows() under the hood.

The code for Lombok.sneakyThrows() is as follows:

public static RuntimeException sneakyThrow(Throwable t) {
    if (t == null) throw new NullPointerException("t");
    return Lombok.<RuntimeException>sneakyThrow0(t);
}

@SuppressWarnings("unchecked")
private static <T extends Throwable> T sneakyThrow0(Throwable t) throws T {
    throw (T)t;
}

From what I can make out is that all it is doing is casting it to a java.lang.RuntimeException so the compiler does not complain.

But shouldn't that technically give a ClassCastException?

For example:

public static void main(String args[]) {
    Throwable i = new InterruptedException();
    RuntimeException rr = (RuntimeException) i;
}

The above code results in a ClassCastException.

Then why does the same thing not happen in case of Lombok?

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
ng.newbie
  • 2,807
  • 3
  • 23
  • 57
  • your basic assumption regarding a cast to RuntimeException is wrong. Please see the documentation. – juwil Nov 01 '21 at 14:54
  • See https://www.gamlor.info/wordpress/2010/02/throwing-checked-excpetions-like-unchecked-exceptions-in-java/ which details this hack. – sp00m Nov 01 '21 at 15:07

1 Answers1

1

Notice this is not a cast to RuntimeException, but a cast to a generic parameter T, constrained to Throwable. Because of Type Erasure, this cast cannot be checked. The cast is a completely unchecked cast.

The runtime doesn't know what T is, except that it extends Throwable. So it can't check whether t actually is a T, and throw an exception if it is not. All it can do is check if t is Throwable, but t's type is already Throwable, so there's no need to do that either.

Normally, you would see a warning about this unchecked cast, and that is what @SuppressWarnings("unchecked") is suppressing.

Sweeper
  • 213,210
  • 22
  • 193
  • 313