Skip to main content

Swing in a better world: Checked exceptions

Posted by alexfromsun on March 21, 2011 at 11:33 AM PDT

Checked exceptions are painful. I could write a long article about it,
but there are more than enough good blogs describing this problem.
My favorite article is written by Rod Waldhoff.




From my point of view the existence of the InvocationTargetException clearly shows the problem.

Imagine you call a method which throws an exception, you properly catches it


and then you decide to rewrite the code and call the same method via refection.


After that you have to catch the same exception in quite a different manner:

public class Test {

   
public static void main(String[] args) {
        PrinterJob job = PrinterJob.
getPrinterJob();
       
       
// Call it in the usual way
       
       
try {
           
// this throws checked PrinterException
            job.print();
        }
catch (PrinterException e) {
           
// handle the printer exception
        }

       
// Call the same method via reflection

        try {
           
// this throws InvocationTargetException instead
            getPrintMethod().invoke(job);
        }
catch (IllegalAccessException e) {
            e.printStackTrace();
        }
       
       
catch (InvocationTargetException e) {
           
if(e.getCause() instanceof PrinterException) {
               
// handle the printer exception;
                // a little bit more complicated than it used to be
            }
        }
    }

   
static Method getPrintMethod() {
       
try {
           
return Class.forName("PrinterJob").getDeclaredMethod("print");
        }
catch (Exception e) {
            e.printStackTrace();
        }
       
return null;
    }
}

I would love it if Java doesn't require me to catch the InvocationTargetException
and let me work with the PrinterException the same way.





Let me give you some examples that illustrate what kind of mess it brings to the Swing code base.



Fortunately there are not many checked exceptions used in Swing code,
but when you implement printing you can't avoid PrinterException
and PrinterAbortException.


Since printing must be done in the background to not block the GUI,

we have to properly re-throw the exceptions back to the Event Dispatching Thread thread.




So in the implementation of the JTable.print() method you can find the following construction:

        // this runnable will be used to do the printing
        // (and save any throwables) on another thread
        Runnable runnable = new Runnable() {
           
public void run() {
               
try {
                   
// do the printing
                    job.print(copyAttr);
                }
catch (Throwable t) {
                   
// save any Throwable to be rethrown
                    synchronized(lock) {
                       
printError = t;
                    }
                }
finally {
                   
// we're finished - hide the dialog
                    printingStatus.dispose();
                }
            }
        };

and then we have to understand what kind of exception was thrown and how we can re-throw it back:

        // check the type of error and handle it
        if (pe != null) {
           
// a subclass of PrinterException meaning the job was aborted,
            // in this case, by the user
            if (pe instanceof PrinterAbortException) {
               
return false;
            }
else if (pe instanceof PrinterException) {
               
throw (PrinterException)pe;
            }
else if (pe instanceof RuntimeException) {
               
throw (RuntimeException)pe;
            }
else if (pe instanceof Error) {
               
throw (Error)pe;
            }

           
// can not happen
            throw new AssertionError(pe);
        }

This boilerplate code can't be avoided, you can find the same pattern in the implementation of JTable.print().


If the methods that are called on the background thread threw another checked exceptions,
we would have to add another instanceof checks there.

More checked exceptions - more lines of code.




If there were no checked exceptions in java, that kind of code would be unnecessary,

we could freely re-throw any exception no matter what their type is.


I would definitely remove the checked exceptions from Java.





This was an entry from the Swing in a better world series




Thanks

alexp

Related Topics >>

Comments

It is completely clear that

It is completely clear that calling a method via reflection will add additional exceptions involving reflection. So if "o.m()" throws an exception X, then "invoke(o, m)" would have addional exceptions to catch, BUT the if call is succesful from the reflection perspective, I see no reason why it cannot behave like the original call and indeed throw exception X directly instead of wrapped. But I agree it's not the drama that this blog suggests, just catch the reflection associated exceptions and rethrow the causes.
But this IMHO is not the actual problem of checked exceptions, the real problem is that once you start doing binding between Swing and a business model, and that business model has (as a good boy) implemented checked exceptions, you're screwed. The binding layer is then forced to implement these specific checked exceptions and it hasn't, because it's a generic library.
Checked exceptions should go away. Forbidden code. Put in a small dark room and never be seen again. They severly limit the ability of exceptions to bubble up through the callstack and be handled at the appropriate place. Luckily there are some hacks allowing to throw a checked exception unchecked and in order to patch this design mistake of checked exceptions (one of) these hacks should become a formal approach. (Enough checked exception bashing? ;-)

The same sort of

The same sort of argumentation again: "It does not work for my GENERIC binding framework. it MUST be REMOVED!". But it does work in my GENERIC binding. So what? I think nobody is forced to use checked exceptions. But if one uses it certainly one has a reason (strategy). It may happen that the strategy does not fit you needs then you have a reason for complaints. But it is not caused by the technology (checked exceptions), it is a design issue. The worse case is when checked exceptions are used without any strategy in mind. Again it is not the technology to blame. ;-) It looks to be a very common issue for GENERIC (and VERY GENERIC) frameworks: they do not respect other systems. So what would your generic binding do in case runtime exceptions are thrown from the business layer? Would it passively pass it over to UI? How would UI recognize what exceptions are business related and what are low level (system) errors? Probably sequences of "instanceof" checks will appear here and there. But again how do I know what is business and needs to be shown to the end user? And what is system and needs to be reported in a standard way? One could use checked exceptions for business and the binding could transform those to GENERIC binding exceptions (checked) which is then recognized by UI as business problems (like BindingValidationException). All other issues can be reported as runtime exceptions and thus treated in a standard way by all layers. Yes, you need to ADAPT your generic binding to a particular business layer. "Good" generic binding frameworks allow for an easy adaption. Off cause it is all possible without checked exceptions but perhaps it is not that explicit then.

Let me begin with saying that

Let me begin with saying that I do not use 'my' binding framework, but JGoodies binding. Which is, AFAIK, the first and about as a reference implementation for binding in Java as it gets. But it really does not matter what binding framework I use, any middleware layer suffers from the same problem; they are forced to handle the checked exceptions, which makes them not generic anymore. I'm not quite clear where are going with your reasoning about tailored middleware, but yes, middleware layers are suppose to only handle a piece of middleware logic, oblivious to the content it is handling, and just pass everything through. Just like HTTP is not suppose to do anything with the HTML, it just has to deliver it. Would be a funny world if HTTP would be custom tailored for each website (which conceptually is what you are suggesting).
The other side, the UI, is specifically written for the BM, and yes, it would know what to do with the BM's exceptions. The binding layer cannot transform the exceptions, because it needs to catch them first, which means be custom modified for the BM (or use a lot of reflection). But please show me a binding framework that handles checked exceptions well and I'll switch over.

Swing in a better world:

First of all, reflection calls are not transparently replacing normal method calls. And since Method.invoke(...) has to inform the caller about any exceptions that happened during the actual method call, it has to wrap these to let you differentiate them later from exceptions that happen during the method dispatching in Method.invoke. This would also be the case if we were dealing with unchecked exceptions only. Otherwise, for example, you'd not be able to correctly tell an IllegalArgumentException thrown by the method invoked apart from one that originated somewhere in Method.invoke(...), and thus you'd be unable to correctly handle your IllegalArgumentExceptions.
Secondly, the problematic constructs from JTable.print() would also persist if we were dealing with unchecked exceptions only. The reason is that it is not possible to communicate exceptions between threads, e.g. from a child thread to its parent. So your statement about having to "re-throw" the exception is quite incorrect. It has to be stored in a variable to pass it back to the other thread. And this in turn is the only reason for the awkward instanceof construct at the end - which would otherwise be replaced by multiple catch clauses, and which actually does rethrow everything but the PrinterAbortException and thus could have been written in a much shorter fashion.
Also, your comment about "more checked exceptions - more lines of code" is quite misleading. This would be exactly the same issue for more unchecked exceptions you'd choose to handle. And moreover, it is possible to form class hierarchies from exception classes, to catch and handle alike culprits the same way.
I admit that there are some powerful arguments against checked exceptions, but the examples you provided do not convince me at all.

Very good remark! it is odd

Very good remark! it is odd to hear that sort of complaints from alexp. It sounds like "it does not work for me, so it must be removed". I like checked exceptions and they do help in my projects. I think the design fof the mentioned print() method is not optimal. But the cause is not the usage of checked exeptions.

Swing in a better world:

Actually, I think they are pretty good examples.

Take the case of calling a method through reflection. Why does Method.invoke throw two unrelated checked exception? How hard would it have been to have one common superclass so that the 99% of people who have no interest in differentiating between the two could just deal with one?

And why does one have to go through this crazy dance of catching Error and RuntimeException separately in the other code example? Whose idea was it to put checked exceptions in between the two? Why not have a subclass CheckedException?

I can live with checked exceptions, as long as they are sensibly designed. I never had a lot of grief with IOException, for example. But I dread things like SAXException, which never occur in practice (or at least shouldn't if that subsystem wasn't such a house of cards) and I absolutely loathe cluttering up my code with InterruptedException.

 

cayhorstmann wrote: I can

cayhorstmann wrote:

I can live with checked exceptions, as long as they are sensibly designed. I never had a lot of grief with IOException, for example. But I dread things like SAXException, which never occur in practice (or at least shouldn't if that subsystem wasn't such a house of cards) and I absolutely loathe cluttering up my code with InterruptedException.

Actually, I completely agree with you. It's not checked exceptions per se that are the problem, it is bad design of exception class hierarchies and not knowing when to use checked and when to use unchecked exceptions. Also, often it is missing documentation: if the API you are using and that is declaring to throw a checked exception doesn't give you a clue why that particular exception is raised, in what situations it will appear and what it does signify, your code will probably be unable to handle it, leaving you no other chance than to rethrow.

So, I'd rather put the blame on lazy API designers than on the language feature. And in my eyes, checked exceptions share the advantage of static typing, as they draw your attention to possible errors early in the development cycle and disallow radical changes that would break caller functionality.