Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java SneakyThrow of exceptions, type erasure

Tags:

Can someone explain this code?

public class SneakyThrow {     public static void sneakyThrow(Throwable ex) {     SneakyThrow.<RuntimeException>sneakyThrowInner(ex);   }    private static <T extends Throwable> T sneakyThrowInner(Throwable ex) throws T {     throw (T) ex;   }      public static void main(String[] args) {     SneakyThrow.sneakyThrow(new Exception());   }   } 

It may seems strange, but this doesn't produce a cast exception, and permits to throw a checked exception without having to declare it in the signature, or to wrap it in an unchecked exception.

Notice that neither sneakyThrow(...) or the main are declaring any checked exception, but the output is:

Exception in thread "main" java.lang.Exception     at com.xxx.SneakyThrow.main(SneakyThrow.java:20)     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)     at java.lang.reflect.Method.invoke(Method.java:601)     at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120) 

This hack is used in Lombok, with the annotation @SneakyThrow, which permits to throw checked exceptions without declaring them.


I know it has something to do with type erasure, but i'm not sure to understand every part of the hack.


Edit: I know that we can insert an Integer in a List<String> and that checked/unchecked exceptions distinction is a compile time feature.

When casting from a non-generic type like List to a generic type like List<XXX> the compiler produces a warning. But it's less common to cast to a generic type directly like (T) ex in the above code.

If you want, the part that seems strange for me is that I understand that inside the JVM a List<Dog> and List<Cat> looks the same, but the above code seems to mean that finally we can also assign a value of type Cat to a variable of type Dog or something like that.

like image 347
Sebastien Lorber Avatar asked Dec 26 '12 09:12

Sebastien Lorber


People also ask

What is type erasure in Java with example?

Type Erasure in Java. Generics concept is introduced in Java language to provide tighter type checks at compile time and to support generic programming. The way to implement generics, the Java compiler applies type erasure to: Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded.

Can you throw an exception from anywhere in Java?

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.

How to throw an exception without a helper method in Java?

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.

What are the different types of exceptions in Java?

Java defines several types of exceptions that relate to its various class libraries. Java also allows users to define their own exceptions. Built-in Exceptions. Built-in exceptions are the exceptions which are available in Java libraries. These exceptions are suitable to explain certain error situations.


1 Answers

If you compile it with -Xlint you'll get a warning:

c:\Users\Jon\Test>javac -Xlint SneakyThrow.java SneakyThrow.java:9: warning: [unchecked] unchecked cast     throw (T) ex;               ^   required: T   found:    Throwable   where T is a type-variable:     T extends Throwable declared in method <T>sneakyThrowInner(Throwable) 1 warning 

That's basically saying "This cast isn't really checked at execution time" (due to type erasure) - so the compiler reluctantly assumes you're doing the right thing, knowing that it won't actually be checked.

Now it's only the compiler which cares about checked and unchecked exceptions - it's not part of the JVM at all. So once you've got past the compiler, you're home free.

I'd strongly advise you to avoid doing this though.

In many cases there's a "real" check when you're using generics because something uses the desired type - but that's not always the case. For example:

List<String> strings = new ArrayList<String>(); List raw = strings; raw.add(new Object()); // Haha! I've put a non-String in a List<String>! Object x = strings.get(0); // This doesn't need a cast, so no exception... 
like image 200
Jon Skeet Avatar answered Jan 01 '23 21:01

Jon Skeet