I was going through Java discussion forums and happened to come across an interesting discussion related to Generics. It was about a loop hole in the Java Generics feature that allows a method to throw a Checked Exception without declaring it in the throws clause.
This is definitely going to be freaky and I wouldn’t encourage you to take advantage of this in your development. Initially I thought this could be a loop hole in the Compiler implementation, but later realized that the behavior is inherent with the Generics design.
public class RethrowCheckedException {
public static void rethrow(Throwable t) {//No throws clause
RethrowCheckedException.throwing(t);
}
public static void throwing(Throwable t) throws T {
throw (T)t;
}
public static void main(String[] args) {
try {
throw new Exception("dummy");
} catch (Exception e) {
rethrow(e);
}
}
}
Using the rethrow() method we can make java.lang.Exception to escape from main() without declaring it in its throws clause. The type <RuntimeException> specified while invoking the throwing() method is know as “explicit type argument”. (which is complementary to the type inference discussed below)
Type Inference
To understand how the above thing works, we need to get an idea about type inference and explicit typing. Take a look at the the following example
public class ArrayStore {
public static void store(T[] array, T item) {
if (array.length > 0)
array[0] = item;
}
public static void main(String[] args) {
store(new String[1], "abc"); //ok
store(new String[1], new Integer(10)); //??
}
}
Surprisingly, both the store method invocations shown, compile. Compiler infers T to String for the first store() invocation as both the arguments are Strings, this process is known as “type inference”. We expect the second one to fail as T cannot be evaluated to both a String as well as an Integer. But, the Compiler infers T to a common super class or interface other than java.lang.Object. As String and Integer have Serializable and Comparable as super interfaces, Compiler deduces T to be a Serializable or Comparable and allows compilation. The result of which is an ArrayStoreException at runtime, defying the whole point of Generics and type safety.
How do we resolve this problem? We can use the “explicit type argument” in such cases.
ArrayStore.store(new String[1], new Integer(10));//compilation Error
Explicitly specifying the type argument as String, forces the compiler to evaluate T to String (overriding the compiler’s type inference mechanism) and hence compilation fails.
Now that we got a hang on type inference, we’ll get back to the loop hole.
public static void throwing(Throwable t) throws T {
throw (T)t;
}
The throwing() method defines type <T extends Throwable> which is declared in its throws clause and is no way related to the argument passed. The rethrow() method explicitly specifies the type as RuntimeException, making the method signature of the throwing() method to appear as shown below, at compile time.
public static void throwing(Throwable t) throws RuntimeException {
throw (RuntimeException)t;
}
Compiler evaluates the method signature by replacing the type variable T with its inferred or explicitly specified type, which is RuntimeException in our case. As a result of this Compiler doesn’t enforce exception handling or re-throwing it in the rethrow() method. After compilation and type erasure, all the generic type information is erased as shown below.
public static void throwing(Throwable t) throws Throwable {
throw (Throwable)t;
}
Some thoughts
Type inference or explicit type argument could do some damage when the Generic type information is not captured properly. In the loop hole case, since the exception argument passed to the throwing() method is not related to the Generic type (T), compiler was blind folded. If we modify the method signature as shown below, method invocation fails to compile.
public static void throwing(T t) throws T {
throw (T)t;
}
Similarly, in the ArrayStore example we cannot expect the programmer to invoke the store method by explicitly specifying the type everytime at the calling context. A better way of capturing the generic type information in this case is to establish a relationship between Type variables(T’s), to enforce expected compile time checks.
public staticvoid store(T[] array, S item) { ... }
And interestingly, the following generic type declarations work as expected unlike the ArrayStore example.
public staticsetMap(Map map) { ... } public static addToList(List list, T element) { ...}
addToList(new ArrayList(), new Integer(10)); //fails to compile
Java Language Specification(JLS) 3.0, 15.12.2.7 states “The process of type inference is inherently complex.”
Yes indeed it is really complex to understand type inference. All this appeals for better understanding of the type inference mechanism and how it applies. Probably you can get more information from FAQ by Angelika Langer.
The not-so-documented com.sun.misc.Unsafe (or whatever) class allows you to throw a checked exception without declaring it, too.
Don’t you get a compiler warning on the (T)t bit? I wonder why casting to type parameters would even be allowed.
Hi Ricky,
“sun.misc.Unsafe” looks even worse and is much easier to mess up things. Thanks for revealing this, hope I don’t get obsessed by this.
Yes, Compiler does generate Unchecked Warning if you type cast (T)t, but then this type cast would never result in a ClassCastException. As you said, its true, there is no point in allowing casting to type parameters, as they are going to get erased by the so called “type erasure”.
That’s pretty cool. I haven’t looked at the JLS in a while, but that looks like a compiler bug to me. The RuntimeException bit shouldn’t be substituted-in/propagated-to the throws clause, if I remember correctly. Also, I think you might be misusing the term ‘type inference’ here. I don’t see any type inference in RethrowCheckedException. Anyway, I think this should be seen as a bug in the JLS or the compiler, not the fault of ‘generics’ in general or any particular use of them.
Hi js,
Yes, I think instead it should be read as “explicit type argument” and not “type inference”. As you said, there is no inference in this case. There needs to be clarity on these 2 concepts which are complementary. Thanks a lot for bringing this up. I’ve updated the post to reflect this.
> The RuntimeException bit shouldn’t be substituted-in/propagated-to the throws clause, if I remember correctly
What do you mean by this? Doesn’t the explicitly specified type argument apply to the throws clause?
Let me modify the definition of throwing() method to the following:
def: public <T extends RuntimeException> void throwing(Throwable t) throws T { throw (T)t; }
call: RethrowCheckedException.throwing(t);
I made T to extend from RuntimeException, so that we can avoid explicit type argument in the call. Isn’t this one similar to the one we have discussed earlier. (except that T can substitute any subclass of RuntimeException here)
I would expect the behaviour of both the mentioned cases to be same in terms of method signature evaluation and compilation. This above one also compiles, but the downside is type cast (T)t results in a ClassCastException.
I’ve spent the last couple hours looking at the JLS again and I’m stumped. The JLS is underspecified and ambiguous in some places, but not so much here. It seems like what you’re doing is pretty unambiguously legal. Of course, that’s only after a couple hours looking… It’s pretty hairy in there. There might be some slightly twisted interpretation of the JLS passages that could make this illegal and get us out of this thorny situation, but I don’t see it.
At this point I would normally say ‘JLS bug’ and be done with it, but it looks like a direct consequence of some clearly intentional stuff:
http://java.sun.com/docs/books/jls/third_edition/html/classes.html#303630
In other words, not a ‘bug’ at all, but a (possibly unnoticed) side effect of an intentional design choice.
I don’t think it breaches ‘type safety’, though. It seems more like, having to be prepared that an Error might come up at any moment. I’ve seen (and instantly forgotten) other ways to sneak checked exceptions in the past, through more normal ways than sun.misc.Unsafe (ClassLoaders/Reflection/Proxy/RMI? I forget), and I don’t they ever ‘fixed’ those either. Just another puzzler for the next edition of Click and Hack, I guess? Might even be a useful tool in the Java Guru Arsenal. 😉
I’m definitely going to keep playing around though: I think there might be some more (really scary) things to fall out of this.
OK well… In the end, I guess it’s *kinda* ambiguous, but followed to the letter, I suppose the JLS does indeed forbid this, and the inference/not-inference distinction is crucial:
http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#341335
…does say “let Ai be the actual type arguments inferred for the method invocation”
That’s what it says. And while you are definitely allowed to provide explicit type arguments, I guess they shouldn’t be applied to the throws clause (since they aren’t the ‘actual type arguments *inferred*’… and in your example, you should get:
RethrowCheckedException.java:3: unreported exception T; must be caught or declared to be thrown
RethrowCheckedException.throwing(t);
Whether it’s the JLS’s fault or the compiler, it’s definitely an issue worth resolving; I wouldn’t be suprised if people started (ab)using it.
And off-topic, but while we’re there, I guess…
http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#387003
… should say “one of the exception types of” instead of “listed in”
I got excited there for a bit, because thought I saw a way to make the compiler throw something that wasn’t Throwable, like a String. I didn’t make it very far. Well, actually, far enough: it compiled. But it just gave a ClassCastException at runtime, equivalent to “throw (Throwable)(Object)new String();”.
Hi js,
You seem to have started an exhaustive hunt over JLS. As there are not many situations where the Compiler performs type based verification, we don’t have much trouble with this kind of behavior.
And you can’t throw anything out of a method which is not a Throwable. Had this not been restricted, we could have seen a different dimension of Java. (rather Exception Oriented Programming (EOP) similar to AOP etc.)
So, the moral is you can fool the Compiler where ever you have specific type based verification, by pretending like the expected type.
Hey Deep: of course the compiler doesn’t let you throw non-Throwables, but using your technique you’re able to trick it into treating an Error as a RuntimeException – two otherwise inconvertible types – through their shared supertype Throwable, with no actual runtime cast. (Yeah, your example shows an Exception, but it also works with an Error). So I figured maybe, analagously, we could trick it into treating a String as a RuntimeException, via their shared supertype Serializable. It was worth a shot.
js, You are right, Error had always been there as an option for us. If you find anything interesting on this, keep me updated.
enough of generics,man.
talk something else.
I realize this is an old discussion, but there is a practical use for this construct.
try
{
Class clazz = Class.forName(“some.class.name”);
Constructor ctor = clazz.getConstructor();
Method m = clazz.getMethod(“somefunc”, Integer.TYPE);
Object o = ctor.newInstance();
m.invoke(o, 3);
}
catch (Exception ex)
{
if (ex instanceof InstantiationException ||
ex instanceof IllegalAccessException ||
ex instanceof IllegalArgumentException ||
ex instanceof InvocationTargetException ||
ex instanceof NoSuchMethodException ||
ex instanceof SecurityException ||
ex instanceof ClassNotFoundException)
{
// handle error
}
else
{
rethrow(ex);
}
}
It solves a problem (7 catch blocks doing the same thing) until Java7 comes along and allows something sensible like:
try
{
Class clazz = Class.forName(“some.class.name”);
Constructor ctor = clazz.getConstructor();
Method m = clazz.getMethod(“somefunc”, Integer.TYPE);
Object o = ctor.newInstance();
m.invoke(o, 3);
}
catch (InstantiationException | IllegalAccessException |IllegalArgumentException |
InvocationTargetException | NoSuchMethodException | SecurityException |
ClassNotFoundException ex)
{
// handle error
}