The Source for Java Technology Collaboration
User: Password:
Register | Login help    

Search

Online Books:
java.net on MarkMail:


Generics compiler bug?

Posted by fabriziogiudici on June 13, 2008 at 2:14 AM PDT
I've been using generics for a few years, and I'm happy with them. I'm aware of the issues related to their design, and I agree that they could have been made a bit better (but I understand the trade-offs made for backward compability), but on the whole I consider them quite useful for writing better code. I haven't had any problems so far, but now I got stuck with a thing that looks like a compiler error. Consider this class that I'm using as a transactional decorator (*) in my NetBeans RCP stuff:

package it.tidalwave.bluemarine.persistence;

...

public abstract class TxTask<T, E extends Throwable>
  {
    ...

    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<E> 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:
  1. The thing worked for several weeks, if not months (I remind you, in a NetBeans RCP project)
  2. 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.
  3. 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).

.../TxTask.java:169: unreported exception java.lang.Throwable; must be caught or declared to be thrown
throw (E)t;

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

(*) 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.
Comments
Comments are listed in date ascending order (oldest first)

Thanks Rèmi. Both issues are closed since a few years - so it seems I'm triggering it again... Very strange, I'll try to post a test case to the bug parade.

It's not strange, it's a bug in the compiler architecture so it can't be easily backported. So the bug is corrected in jdk 6 but is not corrected in jdk5.
Also note that jdk6 compiler can generate 1.5 compatible classes using -target 1.5 Rémi

Ops, yes, thanks for the clarification. I can't use Java 6 on my MBP >:-( but I'll try to set up javac -target 1.5 on my CI server, which is a Linux box.

Ok, my Hudson thanks for the advice :-) In my NetBeans RCP project I worked around the problem by adding this in the build.xml file Basically, it overrides the target for compiling the project and makes sure that the TxTask file is compiled alone before the others.

Ok, I'll never be able to escape all the stuff and post the code here... look at the comment at DZone: http://netbeans.dzone.com/news/generics-compiler-bug#comment-4085

This bug is referenced here:
http://bugs.sun.com/view_bug.do?bug_id=6280975 It was fixed when working on JSR 199
http://bugs.sun.com/view_bug.do?bug_id=6199662 Rémi

In my project I used to have problems compiling generics with Java 5 - from files compiling individually, but not together, to even compiler crashes; but they issues gradually went away while I was upgrading to latest version of Java 6. With 6.6 I don't see any of the issues I used to have. Maybe you should try compiling with Java 6. Also using "throw exceptionClass.cast(t)" instead of "throe (E)t" would make the warning go away. Last (and least) - perhaps I don't see a larger picture, but does parametrization on exception type actually give you any benefit? If not, I would suggest to simply avoid it... But again, I may be judging incorrectly based on a short snippet. HTH.

As a sidenote, I suggest declaring the static methods as public static <T> T execute (final TxTask<T, ? extends RuntimeException> task) and public static <T, E extends Throwable> T execute (final Class<E> exceptionClass, final TxTask<T, ? extends E> task) throws E so that you'll be able to easily execute tasks declaring all kinds of RuntimeExceptions.

Thanks for the suggestion, it's one of the things I tried (together with other tricks such as encapsulating the thing in a method, etc...) but it doesn't make the error go away (it's an error, not a warning). The benefit is important, the capability of wrapping any piece of code without changing the type of the exception it may throw.