Unchecked exceptions for IO considered harmful.

732 views
Skip to first unread message

Kevin Burton

unread,
Aug 12, 2016, 6:00:51 PM8/12/16
to mechanical-sympathy
Both the Elasticsearch and Cassandra drivers now use unchecked exceptions.

Personally I consider this harmful. 

It's entirely possible I'm wrong here, but I can't find any sort of definitive/referential explanation why some consider checked exceptions to be harmful / evil.

I've found a few documents but most of these are from junior (IMO) programmers complaining that they don't know what to do with.  Either that or they just call System.exit() ... 

Is there anything authoritative by someone with a decent reputation? Maybe a JVM author or language author clearly making the case of why checked exceptions are bad.

Additionally, it would be nice to see what the checked exception workaround could be.

I was thinking of lambda function like withCheckedException() so that I can make unchecked exceptions become checked ones.


Henri Tremblay

unread,
Aug 12, 2016, 10:34:31 PM8/12/16
to mechanica...@googlegroups.com
That's a big ongoing discussion.

You can read an old blog post from Brian Goetz which is mostly a fan (at least in 2007) of checked exception but pointing out that is was causing some ugly code. It has highly improved in JDK 7 and 8.

http://briangoetz.blogspot.ca/2007/06/remove-checked-exceptions.html

Then you have Josh Bloch, who's talking about it in Effective Java
Use checked exceptions for recoverable conditions and runtime exceptions for programming errors

So, he's ok with them, but in a limited scope. I tend to agree with him. I remember those terrible times when Hibernate was throwing checked HibernateExceptions. That was a really bad decision because when there's an exception in Hibernate, the only thing you want to do is to rollback the transaction. Which normally occurs at a really high level.

It's the same with IOExceptions. In general, there's not much you can do about them.

Finally, a really nice article from Brian commenting Josh, Rod Johnson and Bruce Eckel. They have a decent reputation ;-)

--
You received this message because you are subscribed to the Google Groups "mechanical-sympathy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mechanical-sympathy+unsub...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Kevin Burton

unread,
Aug 13, 2016, 4:30:01 PM8/13/16
to mechanical-sympathy
Thanks.  I've already read the above.  What's disturbing about these for me is that major driver frameworks like Elasticsearch and Cassandra have adopted unchecked exceptions without actually explaining the rational behind their decision.

If you are a fan of unchecked exceptions, fine , please explain why.  

João Nuno Silva

unread,
Aug 13, 2016, 6:23:12 PM8/13/16
to mechanica...@googlegroups.com

This page explains aws sdk reasoning: http://docs.aws.amazon.com/AWSSdkDocsJava/latest/DeveloperGuide/java-dg-exceptions.html

>>> To unsubscribe from this group and stop receiving emails from it, send an email to mechanical-symp...@googlegroups.com.


>>> For more options, visit https://groups.google.com/d/optout.
>>
>>

> --
> You received this message because you are subscribed to the Google Groups "mechanical-sympathy" group.

> To unsubscribe from this group and stop receiving emails from it, send an email to mechanical-symp...@googlegroups.com.

Dan Eloff

unread,
Aug 13, 2016, 7:55:54 PM8/13/16
to mechanica...@googlegroups.com
One can also look at how other statically typed languages since Java implement exceptions.

There's Anders Hejlsberg and Bruce Eckel discussing why C# doesn't have checked exceptions: http://www.artima.com/intv/handcuffs.html

Go (and it seems to me Rust as well) use explicit error returns for recoverable failures and panic for programming errors or unrecoverable errors. Panic is similar to an unchecked exception in that it unwinds the stack and aborts the program if no handler is encountered.

IO errors are explicit in Go, which has the same problem as Java checked exceptions. Typically those errors are handled much higher up, if at all. So the error propagates up the return signatures of the call stack, along with a lot of boilerplate:

n, err := file.Write(bytes)
if err != nil {
    return err
}

Rust has a try! macro that expands to pretty much the same code as above, which is nice because it eliminates the boilerplate at least.

There's no shortage of articles complaining about the Go approach to error handling. The major drawback is all the error handling code to propagate the error back up the stack in the return values. The major benefit is the error handling is explicit so that a reader of the code can see where errors come from and how errors flow through the program. Go strongly optimizes for the reader of the code rather than the writer, so it's easy to see why they chose to do it that way.

It's the same with checked exceptions I think, both a curse and a benefit. One additional concern with checked exceptions is that introducing or removing an exception is a breaking change. So adding a new checked exception breaks all callers of the method, even if most of the time they don't care and the exception would just be caught at the top level with a catch all anyway.

Go avoids this problem if the function already had an error return since the convention is to return a generic "error". At any time you can add a new error return to the function, and as long as the error type implements the error interface (the Go equivalent to deriving from Exception), no signatures change and the calling code already handles the error condition just fine most of the time.

Even after so many years it's still a contentious issue with no right answer for every situation. Sometimes it could make sense, sometimes it doesn't, and it's often prone to abuse, e.g. just marking methods as "throws Exception".







To unsubscribe from this group and stop receiving emails from it, send an email to mechanical-sympathy+unsubscribe...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Henri Tremblay

unread,
Aug 13, 2016, 10:06:20 PM8/13/16
to mechanica...@googlegroups.com
In fact, my guess is that you have checked exception haters that will not stop complaining unless you use unchecked exceptions.

Then, you won't find much checked exception lovers. We will find some "I think they can be useful" (like me) and that won't complain too much if unchecked exception are used.

So... unchecked it is. It removes 2 issues a week from github :-) (and yes, I'm kinda serious here).

That said, I will also say that developers hate error management. They never take care of it correctly. Nobody cares, nobody test them, etc etc. So, if you want your framework to be sexy, you better prevent having tons of try catch in your demo code. They don't want to think about error management right now (and probably never will), they want to use your framework.

So unchecked it is.

And finally, everyone swear when writing

InputStream in = null;
try {
   in = new FileInputStream(...);
   ...
}
catch(IOException e)  {
    throw new RuntimeException(e); // because I have no idea what to do with it
}
finally {
   try {
      if(in != null) in.close();
   }
   catch(IOException e1) {
      // let`s silently ignore assuming the world is still turning
   }
}

So when you think about checked exception, the first image that comes in my head is IOException... Then I fell sad and tell myself that unchecked exception is probably a good default.

Psychology aside, my way of picking between checked and unchecked is: "Will someone frequently do something with this exception? Apart from rollbacking the transaction" If the answer is yes, if is checked. Then, depending to who you are talking to, the word "frequently" might be replaced by "ever". It means this person likes checked exceptions more than I do. But he should still use the question for every exception he throws.

I hope it helps.
Henri


To unsubscribe from this group and stop receiving emails from it, send an email to mechanical-sympathy+unsubscribe...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Eric DeFazio

unread,
Aug 15, 2016, 9:47:00 AM8/15/16
to mechanica...@googlegroups.com
<< Sorry I delete this -- wrong forum >>

Vitaly Davidovich

unread,
Aug 15, 2016, 10:01:00 AM8/15/16
to mechanical-sympathy
Why is it egregious? It's detailed in the types of exceptions it throws, yes, but that's good assuming you want to handle some of those types (and there are cases where those exceptions can be handled properly).  Even before ReflectiveOperationException, you could use multi-catch since Java 7 to get a bit more ergonomics.  IMHO, it's better than "throws IOException" -- that tells me nothing, really.  Is it the disk/media is full? Is it because file/dir not found? Is it because I don't have permissions? The javadoc for it says "Signals that than I/O exception of some sort has occurred...", emphasis mine.  Granted, there are subtypes and some methods throw checked subtypes.  But just "some sort" is nearly worthless since some exceptions may be handled properly, whereas others denote a case where you cannot continue.

I do agree that checked exceptions are tricky to use right, mostly because they require the same rigor and thinking that defining the rest of the API requires (e.g. what return types are returned).  Once you define an API to return Foo, you're on the hook for returning Foo (or subtype) or else you break code.  Exceptions, checked or not, are just another return value from a method.  One can return Object, and then not worry about breaking compilation either, but most code doesn't do that.  So, it helps to think of checked exceptions as a typed return value from a method, and give it the same type of diligence as the rest of an API.

On Mon, Aug 15, 2016 at 9:47 AM, Eric DeFazio <edef...@gmail.com> wrote:
IMHO the most egregious use of ChekedExceptions is in Javas Reflection API, and I cry when I think how beautiful it could have been verses how cumbersome reflection can be (My SmallTalk friends laugh about it)

(To construct an instance of an object and then call a method on it)

try
        {
           Class clazz = Class.forName( className );
           Class[] ctorParams = {String.class, String.class, Integer.TYPE};
           Constructor ctor = clazz.getConstructor(ctorParams);
           Object person = ctor.newInstance("lastName", "firstName", 20 );
           Method toStringMethod = clazz.getDeclaredMethod(methodName);
           System.out.println( toStringMethod.invoke(person) );
        }        
        catch( ClassNotFoundException cnfEx )
        {
        }
        catch( NoSuchMethodException nsmEx )
        {
         
        }
        catch( IllegalAccessException illAccEx )
        {
         
        }
        catch( InvocationTargetException invokeTargetEx )
        {
         
        }
        catch( InstantiationException ctorEx )
        {
         
        } 
Granted, this changed in Java 7 (all those exceptions are subclasses of ReflectiveOperationException)
try
        {
           Class clazz = Class.forName( className );
           Class[] ctorParams = {String.class, String.class, Integer.TYPE};
           Constructor ctor = clazz.getConstructor(ctorParams);
           Object person = ctor.newInstance("lastName", "firstName", 20 );
           Method toStringMethod = clazz.getDeclaredMethod(methodName);
           System.out.println( toStringMethod.invoke(person) );
        }        
        catch( ReflectiveOperationException re )
        {
        }
But still, I think given how widely used a language feature like Reflection is, and how many Libraries, Tools, and Frameworks use Reflection... this was just a terrible API design (along with JDBC)

Avi Kivity

unread,
Aug 15, 2016, 10:05:46 AM8/15/16
to mechanica...@googlegroups.com

Perhaps this discussion should be moved to a Java group.  As far as I can tell, it has nothing to do with mechanical sympathy.

To unsubscribe from this group and stop receiving emails from it, send an email to mechanical-symp...@googlegroups.com.

Martin Thompson

unread,
Aug 15, 2016, 10:07:10 AM8/15/16
to mechanica...@googlegroups.com
+1

Peter Lawrey

unread,
Aug 15, 2016, 5:13:36 PM8/15/16
to mechanical-sympathy
I agree mostly with what you are saying exception we mostly handle and recover from IOException in our code. In fact we throw EOFException in so places so it can be caught higher up in code.

Peter Lawrey

unread,
Aug 15, 2016, 5:40:29 PM8/15/16
to mechanical-sympathy
I recently did an analysis of libraries and came up with this breakdown of how we handle Exceptions. (See below)

Our view is that Checked Exception makes more sense for library writers as they can explicitly pass off errors to the caller.
As a caller, especially if you are new to a product, you don't understand the exceptions or what you can do about them.  They add confusion.

For this reason we use checked exceptions internally in the lower layers and try to avoid passing them in our higher level interfaces.

Note: A high percentage of our fall backs are handling iOExceptons and recovering from them.




Exception handling mix.png

Peter Lawrey

unread,
Aug 15, 2016, 5:42:42 PM8/15/16
to mechanical-sympathy
My experience is that the more complex and layered your libraries the more essential checked exceptions become.  I see them as essential for scalability of your software.

>>> To unsubscribe from this group and stop receiving emails from it, send an email to mechanical-sympathy+unsub...@googlegroups.com.


>>> For more options, visit https://groups.google.com/d/optout.
>>
>>
> --
> You received this message because you are subscribed to the Google Groups "mechanical-sympathy" group.

> To unsubscribe from this group and stop receiving emails from it, send an email to mechanical-sympathy+unsub...@googlegroups.com.

Martin Thompson

unread,
Aug 15, 2016, 5:48:28 PM8/15/16
to mechanica...@googlegroups.com
This thread is now way off the topic of mechanical sympathy. 

Please lets keep it focused.

Martin...

Message has been deleted

Peter Lawrey

unread,
Aug 15, 2016, 8:40:33 PM8/15/16
to mechanica...@googlegroups.com
Why would you create multiple catch blocks unless you have different handling code.

    try {
         clazz.getDeclaredMethod(methodName)
              .invoke(Class.forName(className)
                           .getConstructor(String.class, String.class, Integer.TYPE)
                           .newInstance("lastName", "firstName", 20 ));

    } catch(OnlyCatchASpecificExceptionIfYouNeedTo ee) {
         // I want to handle this one differently.

    } catch(Exception e) {
         // handle them all the same
    }


--
Reply all
Reply to author
Forward
0 new messages