Tuesday, May 24, 2011

The Tragedy Of Checked Exceptions

If you ever get one of those interview questions along the lines of "What DON'T you like about Java?", I would hope that checked exceptions are at the top of your list. I think no other, ahem, feature, of Java has caused more code bloat, more problems, and less stability than checked exceptions. Java was the first main-stream language to include this concept (to my knowledge), the only (widely used) programming language that has it, and I strongly hope it will be the last programming language to include it.

Checked exceptions cause a lot of grief. They often pollute APIs: look at how the JDBC API plays "cover your ass" by making every single method throws JDBCException regardless of which methods do any work that could possibly fail. At best, checked exceptions make sense only when there is a clear and documented way to recover from the exception (such as waiting and retrying the failed operation). In many cases, a simple boolean, indicating success or failure, would be much better than an exception, and accomplish the same goals with far less cost.

Checked exception's also encourage another terrible pattern: exception swallowing. "What do I do with this here MyAPIIsBrokenException? Well Eclipse just inserts code to print out the stack trace, so that's good enough." Thus real errors get discarded, and code that should break during testing slips through the cracks, causing nasty runtime failures and ominous messages to the console.

Really, what can you do with an exception? Either handle it locally and immediately, or wrap it in another exception, usually RuntimeException, and rethrow it ... but that approach is only effective if some higher layer does a good job of reporting the entire stack of exceptions, the way Tapestry does. More often, the exception just percolates up to a top-level loop and spews out a few hundred lines of glop onto the console or log.

I think part of the proof that checked exceptions are simply unworkable is the way throws Exception is creeping into standard APIs, such as the ones specified in project Coin (I'm thinking of Autocloseable). And what is the semantic value of throws Exception? It's useless ... because you are either going to log that exception to the console or wrap it in a new RuntimeException and re-throw it. So the authors of Autoocloseable have simply shifted work onto your lap (you get to write the code to catch it and rethrow it) when if they simply omitted the throws clause, and documented that "close() methods may throw a runtime exception" you could get the exact same effect, but write much less code.

I've also seen that checked exceptions have been a factor in the delays for JDK 8 Lambdas, complicating that specification much further than it needed to be, and forcing new and odder syntax into the language to accompany it.

Meanwhile, post-Java JVM languages ... including Groovy, Fantom, and Clojure ... simply ignore checked exceptions; which is easy enough to do as they are almost entirely a fiction of the Java compiler in the first place. You can write try...catch blocks in these languages, but there's no pressing need to, and application stability ends up being higher than in traditional Java code.

It is unfortunate that of all the ideas that Gosling, Joy, and folks had at the dawn of the Java language, they deferred ones we've really been missing (such as reified types and lambdas) and included truly experimental features, such as checked exceptions. But that's just hind-sight and second-guessing. The real tragedy is that, unlike (for example) JavaScript, with Java you can't just use the good parts. Instead, Java snares you with an almost irrational compulsion to preserve the early mistakes in the language, forever.

Monday, May 23, 2011

Learn more about Tapestry power features @ Java Magic

New Tapestry user "tawus" has been working his way though the power features of Tapestry and Tapestry IoC on his blog: Java Magic. It's an interesting approach, he's covering one feature of Tapestry on each post, and showing what a (simplified) implementation of that feature would be. I'd rather he spent a little introduction time on why these features are useful and important, but it's still a very nice effort.

Friday, May 20, 2011

Extending JavaDoc

I don't think I've seen a piece of code more poorly designed for extension, and more in need of it, than JavaDoc. I'm in the process of removing Tapestry's Maven-based component report (as part of an overall move from Maven to Gradle). My goal is to merge what currently comes from the component report directly into the JavaDocs.

My first approach was to extend the built-in HtmlDoclet , the one that generates the every-day HTML report. Good luck with that ... it's like a field guide to anti-patterns for preventing extensibility. Here's an example:

public class HtmlDoclet extends AbstractDoclet {
    
    /**
     * The global configuration information for this run.
     */
    public ConfigurationImpl configuration = 
        (ConfigurationImpl) configuration();
    

    /**
     * Create the configuration instance.
     * Override this method to use a different
     * configuration.
     */
    public Configuration configuration() {
        return ConfigurationImpl.getInstance();
    }

    ...
}

public class ConfigurationImpl extends Configuration {

    /**
     * Constructor. Initialises resource for the
     * {@link com.sun.tools.doclets.MessageRetriever}.
     */
    private ConfigurationImpl() {
        standardmessage = new MessageRetriever(this,
            "com.sun.tools.doclets.formats.html.resources.standard");
    }
    
    public static ConfigurationImpl getInstance() {
        return instance;
    }

   ...
}

So, HtmlDoclet commits the cardinal sin of down-casting from the interface to the implementation class, and ConfigurationImpl is effectively final, as its only constructor is private. But you are encouraged to override the configuration anyway (recommended only if your goal is to throw a ClassCastException).

JavaDoc is old. The HtmlDoclet is just ... tired. Someone failed to tell these folks about XML and XSL, for example ... or about HTML and CSS, for that matter. JavaDoc is screaming out to be a tool that generates an XML representation of Java source content that can then be transformed into an HTML document tree via XSLT. I've seen an abandoned project along those lines. Perhaps in my spare time ... it would be a fun little side project to create that, and create a really world-class JavaDoc.

In any case ... I've been forced to impose the use of a @tapestrydoc tag on component classes that wish to be documented. Not the end of the world, but not backwards compatible either (though the Tapestry 5.2 Maven component report will continue to work with Tapestry 5.3, so that's not a deal-breaker).