Generics Compiler Bug?
package it.tidalwave.bluemarine.persistence;
...
public abstract class TxTask
{
...
public abstract T run()
throws E;
public static <T> T execute (final TxTask<T, RuntimeException> task)
{
return execute(RuntimeException.class, task);
}
public static <T, E extends Throwable> T execute (final Class exceptionClass,
final TxTask<T, E> task)
throws E
{
try
{
...
try
{
result = task.run();
...
}
catch (RetryException e)
{
...
}
catch (PersistenceException e)
{
...
}
}
catch (Throwable t)
{
...
if (exceptionClass.isAssignableFrom(t.getClass()))
{
////////////////////// THE PROBLEM IS HERE
throw (E)t;
////////////////////// THE PROBLEM IS HERE
}
throw (t instanceof RuntimeException) ? (RuntimeException)t : new RuntimeException(t);
}
...
}
...
return result;
}
}
The peculiarity of this class is that it uses a generic in
the "throws" clause. This is ok with the language specifications, see
for instance the excellent Angelika
Langer's tutorial on generics (even
though it's considered pretty useless in most cases, since you can't
throw a generified exception because of type erasure - but in my code
things are ok since I'm just rethrowing).
Now, the history of this issue is pretty complicated:
- The thing worked for several weeks, if not months (I remind you, in a NetBeans RCP project)
- At a certain point, I copied the class in the prototype of a web application, and I got a compiler error (see below); since I was focused on other things, I just commented out the offending part. blueMarine was still compiling ok.
- Since a few weeks, this is triggering the compiler error even in the NetBeans RCP project: but ONLY IF I BUILD THE WHOLE PROJECT (that is, if multiple *.java files are compiled at the same time). If I select the single TxTask.java file and compile it with the pop-up menu, it gets compiled ok (in fact, with this trick blueMarine builds, runs and passes the tests).
This happens with the Java 5 compiler both on Mac OS X and Ubuntu Linux. I've asked for help in a couple of mailing lists so far, without success. So if you have any hints, please help: it is causing major pain, since it's breaking the CI on Hudson. The full source of the class is here: https://bluemarine.dev.java.net/svn/bluemarine/trunk/src/blueMarine-core/Core/Persistence/src/it/tidalwave/bluemarine/persistence/TxTask.java
.../TxTask.java:169: unreported exception java.lang.Throwable; must be caught or declared to be thrown
throw (E)t;
(*) I'd like to get rid of this class sooner or later and use Spring or EJB3 annotations for transactions - but this won't happen before the next release, and I need to have CI working before that.
From http://weblogs.java.net/blog/fabriziogiudici/
- Login or register to post comments
- 2219 reads
- Flag as offensive
- Email this Story
- Printer-friendly version
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)







Comments
David replied on Mon, 2008/06/16 - 2:27am
Walter Laan replied on Mon, 2008/06/16 - 3:11am
Edit: Oh looks like the blog to javalobby stripped all the generic info form the code, comments on the blog suggest using 1.6 compiler which has the fix for http://bugs.sun.com/view_bug.do?bug_id=6280975 with target 1.5.
The code posted does not compile at all, neither T or E are declared. Do you have the problem using it as follows?
public abstract class TxTask<T, E extends Throwable> {
public abstract T run() throws E;
public static <T, E extends Throwable> T execute(final TxTask<T, E> task) throws E {
return execute(null, task);
}
public static <T, E extends Throwable> T execute(
final Class<E> exceptionClass, final TxTask<T, E> task) throws E {
T result = null;
try {
try {
result = task.run();
}
catch (Exception e) {
}
}
catch (Throwable t) {
if (exceptionClass != null && exceptionClass.isInstance(t)) {
throw exceptionClass.cast(t);
}
throw (t instanceof RuntimeException) ? (RuntimeException) t : new RuntimeException(t);
}
return result;
}
}
Uffe Seerup replied on Mon, 2008/06/16 - 8:51am
It appears that you have been bitten by type erasure. You can not cast to a type defined by a type parameter (nor can you ever catch an exception using a type parameter as the type of the exception). The reason for this is that the type is not known at runtime, since it has been erased.
I would also question your assertion that you understand the trade-offs made for backward compability. The fact that you refer to the issue as backwards compatibility sort of gives you away. It was never backwards compatability which mandated type erasure, but a much more esoteric term reffered to (by Gafter) as migration compatibility. It was entirely possible to have designed reified generics (i.e. no type erasure) and maintained backwards compatability.
Type erasure was only mandated in extreme cases where a customer would be using multiple libraries 1) to which he didn't have source access to 2) were supplied from separate vendors 3) where the vendors had not isolated their usage of collections by using interfaces 4) where some of the vendors made compelling versions only on Java 1.5+ while other vendors made compelling new versions only on 1.4, 5) the vendors relied directly upon eachother - i.e. not integrated through source code under control of the customer and 6) the customer could not possibly wait for the vendors to get their libraries in synch. Only when all of the above applied would usual "backward" compatibility not apply.
That type erasure was needed because of backwards compatibility is a myth. Pure and simple. The constraint which could not be solved with reified generics were a niche constraint which IMO is unlikely to have ever benefitted anyone but lazy VM vendors. .NET had a similar situation and created reified generics and implemented new generic collection classes which simply implemented the old-style (non-typesafe) interfaces with runtime casts, thus not only allowing old bytecode to run unmodified on the new VM but also allowed new generic collections to be passed to old code.
Geertjan Wielenga replied on Mon, 2008/06/16 - 10:08am
in response to: walter laan
Edit: Oh looks like the blog to javalobby stripped all the generic info form the code
Thanks, fixed that.
Fabrizio Giudici replied on Mon, 2008/06/16 - 12:39pm
Fabrizio Giudici replied on Mon, 2008/06/16 - 5:37pm
Daniel Spiewak replied on Tue, 2008/06/17 - 11:11am