RuntimeExceptions – To try-catch or not to catch?
10kloc.wordpress.com
10kloc.wordpress.com
This post is clearly written by someone who is new to Java, and starts with the antique assumption that the runtime/checked exception dichotomy is a good idea. After nearly two decades of experience, programmers and language designers have resoundingly voted this language design feature to be a failure. A little bit of experience catching idiotic exceptions like UnsupportedEncodingException and you start to see why. But it goes deeper than just bad design in the standard libraries - checked exceptions fundamentally violate interface encapsulation - try throwing a meaningful exception through Runnable or Iterator. The net result is stacktraces with dozens of wrapped exceptions that destroy any hope of meaningfully handling known error conditions.
Stop it. JUST STOP IT. Checked exceptions have wasted hundreds of hours of my time, not just writing lame wrappers so that I don't have to type try/catch on every line of code, but also by making debugging and error handling five times more painful than it should be.
If you're still touting checked exceptions in 2013, you are part of the problem. Stop it. Java needs to evolve, and your fresh-from-1995 opinion is not helping.
TL;DR: Of course you should catch RuntimeExceptions. There should be no other kind of exception.
You have that backwards. Unchecked exceptions will blithely and without warning completely explode your stack. They're the Atomic Goto.
The only way to know whether you're going to get one is to check the documentation, where you can only hope that the API author -- and the author of every API he calls -- has actually documented the exceptions that get thrown, because the compiler will be no help whatsoever, and god forbid an API start throwing an exception later.
> ... try throwing a meaningful exception through Runnable or Iterator.
If you pass around an object that conforms to Iterator, but throw an exception within it, __YOU'RE BREAKING THE API CONTRACT.__
Anyone that relies on the API contract of the Iterator class could have their state completely walloped by the Atomic Goto you inserted with a runtime exception.
> Stop it. JUST STOP IT. Checked exceptions have wasted hundreds of hours of my time, not just writing lame wrappers so that I don't have to type try/catch on every line of code, but also by making debugging and error handling five times more painful than it should be.
This makes no sense, because more work is required without checked exceptions.
Without checked exceptions:
- You must check the API docs for every line of code you write to see if it will throw an exception, and if so, what types.
- If it throws an exception, you must either add a try/catch block to handle the error appropriately (or wrap it in a new exception type), or you must add documentation to your method to declare that it will bubble up an exception, and of what type.
- If new exception types are thrown by underlying code, nothing tells you until your application explodes.
With checked exceptions:
- The compiler tells you what exceptions code throws, and of what type.
- If it throws an exception, you must either add a try/catch block to handle the error appropriately, or declare it in the method prototype. The IDE will do both of these things for you.
- If new exception types are thrown by underlying code, the compiler will warn you.
> If you're still touting checked exceptions in 2013, you are part of the problem. Stop it. Java needs to evolve, and your fresh-from-1995 opinion is not helping.
Stop advocating broken API design and ignorance of API invariants.
Point by point:
Unchecked exceptions will blithely and without warning completely explode your stack.
No. They rewind back to the level at which you catch the appropriate exception - possibly Exception. It's true that you aren't always able to catch each exception as specifically as you want. But at least you have a hope of doing that instead of catching RuntimeException and searching back through a dozen nested exceptions, which is today's reality.
In actual practice, there's usually a place you can (and should) catch Exception so that you can log and either retry or abandon the unit of work. In the context of a web server, this is usually "one request". That's why your appserver doesn't crash when something goes wrong.
This isn't theory, your appserver already does this, and it's a wonderful thing. Try doing some node.js programming sometime for a taste of what it's like without that.
Your mistake is believing that there is an alternative to RuntimeException. There is not.
If you pass around an object that conforms to Iterator, but throw an exception within it, __YOU'RE BREAKING THE API CONTRACT.__
Again, your mistake is believing there is an alternative. With the exception of very simple classes, all code has exceptional conditions. Do you create DatabaseIterator which throws DatabaseException (and NetworkException) every time you want to iterate a result set? Then you can't use it with Guava or any of the other 10 million Java libraries that consume standard interfaces, and you might as well not have interfaces at all.
Only pedants care if the compiler checks every single exception. Real-world programmers expect that code will complete normally, and catch exceptions at a high enough level that they can do something useful about it (eg, retry or return an error code to the client).
Exceptional conditions cannot be part of the API contract, because every body of code has different exceptional conditions, and if you try to account for them, you have one of two results:
* You give up on standard interfaces completely. You have DatabaseIterator (which doesn't extend Iterator), FileIterator (which doesn't extend Iterator), etc.
or:
* You throw IteratorException from each method. This is what the Java SDK ends up doing in various places (eg, InvocationTargetException) which is just plain retarded. It would make more sense (and be less annoying) to just throw Exception and leave off the wrapper exception.
You must check the API docs for every line of code you write
Sorry, you are not excused from this, ever. At least with exceptions, you have the option of ignoring the problem at deep levels and catching them higher up - which you already have to do anyways, because nobody wants their app to crash at the first exception. In webapps or appservers it's at the request level; in GUI apps it's usually the UI event handler.
With checked exceptions:
No, the compiler doesn't do any of that shit. Because in the Real World, you end up passing data through a standard interface and the exception gets wrapped. Don't believe me? When was the last time you saw a stacktrace that wasn't wrapped with several Caused by clauses? With the exception of remote API calls (where the exception class may not exist on the client), Caused by: is a fundamental failure of language design. There's no sane reason for this construct to exist. Try writing code in other languages and feel how refreshing it is to get clean stacktraces. You don't even have to leave the JVM, you just have to use any of the languages designed since 1995.
This subject has been hashed to death, we're repeating the same arguments. You want to leave things ambiguous, I want to ensure that behavior is well-defined.
You're right that this has been abandoned by other languages. In many of those languages -- including Scala -- you can make use of Either/Option monads to achieve the same effect without exceptions at all. In others, you just have to either expend a bunch of mental effort to ensure you code defensively, or take your approach of spinning the chamber and hoping for the best.
Your argument for "well defined points" only begins to make sense in purely functional languages. Otherwise, there's no way to guarantee a lack of side-effects in your use of atomic-goto, and the result is invariably that the software is unreliable and prone to strange stateful failure.
That said, a standard clean usage model for the checked variants would be to declare a 'throws' from your JSON parsing code, and then at a higher level wrap that in an InvalidMessageException or similar. The non-checked approach would be to check for NULLs and ensure that none escape your parsing code.
The only time the checked version becomes burdensome is if you don't encapsulate your JSON -> app model parsing in a single place, and instead sprinkle the code with unencapsulated JSON parsing. In that case, I believe the problem is a lack of encapsulation, not the checked exceptions -- the exceptions just highlights that one is not separating concerns between code that must be aware of parsing issues and potentially invalid data, and code that can be guaranteed to only operate on valid and whole data.
That was uncalled for. But, since you've decided to immediately call into question people's credentials, let me start with mine. I've done a little over 30 years of software development, and in that time I've programmed in no less than three assembly languages, several BASICs, Pascal, C, C++, JavaScript, dabbled with C#, dabbled with Go, dabbled with Python, been subjected to some Perl and PHP, etc. I'm not God's Gift to Software Development, but I think I have enough experience that my opinion on this matter shouldn't be discarded as uninformed.
"and starts with the antique assumption that the runtime/checked exception dichotomy is a good idea."
I hope you're aware that you're begging the question. In any case, I think checked exceptions are fantastic.
1. The "burden" of dealing with them is minuscule, especially when using modern IDEs. It takes just a few clicks or key presses to add try/catch blocks or re-throw exceptions as necessary. You don't even have to waste time looking up documentation (which may be wrong or even nonexistent). Your tool-chain (IDE and compiler) handles the mundane details for you.
2. It forces developers, who are often hurried, and who often make mistakes, to think about things that could go wrong, and deal with them (one way or another). Oh, sure, you see plenty of "catch (Exception e)" style abuses, but that particular "code smell" is so strong (i.e., easily identified) that it can make code reviews (personal and public) easier. Code reviews in languages with only unchecked exceptions are much more difficult and time consuming.
3. Checked exceptions speeds development. Much less time is spent reading and re-reading API documentation to make sure you've caught all the possible exceptions that might be thrown (so that you can do things like produce meaningful error messages or log entries based on the exception thrown).
4. Unchecked exceptions are a production run-time nightmare. My anecdotal but extensive experience is that languages with only unchecked exceptions cause far too many costly (in both time and money) production headaches. Never, ever defer until run-time, errors that can be caught at compile-time! Admittedly, it could be that my application domain colors my opinion here (I work on large systems, not small systems).
The best argument I've heard against checked exceptions is that they "bloat the code". It's true: checked exceptions requires more code. But I'm writing code, not poetry. My job is to get the implementation right (which checked exceptions help me do) and to write reliable and robust software. Developers who put more emphasis on writing pretty code (sorry, "highly expressive") might want to consider becoming artists instead, where they can fashion "pretty" creations to their heart's content.
"It forces developers, who are often hurried, and who often make mistakes, to think about things that could go wrong, and deal with them"
Checked exceptions won't solve your problem here at all. If you need an instrument to force developers to think about things that could go wrong your team is screwed anyway.
"Much less time is spent reading and re-reading API documentation to make sure you've caught all the possible exceptions ..."
You are never ever going to catch all possible exceptions anyway (runtime exceptions and errors are still there even if you'd prefer to have checked exceptions only).
"Unchecked exceptions are a production run-time nightmare."
And you have to deal with them even if you use checked exceptions (again runtime exceptions and errors are still there). You have to have proper (unchecked) exception handling in the right places in your software.
Are you actually properly producing tests for your code before deployment-time ? Otherwise I would not get it why it would be nightmare.
"The best argument I've heard against checked exceptions is that they "bloat the code". It's true: checked exceptions requires more code.
Bloat causes development drag and makes the code more difficult to understand (more scrolling, jumping to different locations in the same file, etc.).
"But I'm writing code, not poetry."
I think you produce both.
I'd compare here checked exceptions to unchecked exceptions like what staticlly typed languages are to dynamically typed languages. And I've seen a lot of developers "loving" statically typed languages because the compiler tells them all their errors (at least they think so).
It isn't about needs, it's about programming languages making it easier to write robust and reliable software. This benefits all developers, from inexperienced to masters. Even the best developers make mistakes and sometimes forget to check return codes.
Checked exceptions are just one useful tool in the toolbox. Another useful tool is dynamic strings (so that developers don't have to waste wetware cycles worrying about overflowing fixed size string buffers). Another useful tool is garbage collection (so that developers don't have to waste wetware cycles worrying about where and when to free memory).
These features lead to higher productivity and more reliable and robust software. Why wouldn't you want a tool that makes sure you check your return codes? Because it might force you to write "throws FooException" on your method signature, if you choose to handle that higher up the call stack? Oh, what a burden!
"You are never ever going to catch all possible exceptions anyway (runtime exceptions and errors are still there even if you'd prefer to have checked exceptions only)."
This is not a good argument against checked exceptions, this is an argument for using the appropriate type of exception for your current needs. Checked and unchecked exceptions are both useful tools.
Yes, sometimes developers use checked exceptions when they should use unchecked exceptions, and vice versa. And sometimes people use a big screwdriver when they should be using a hammer. Does that make screwdrivers or hammers useless?
"And you have to deal with them even if you use checked exceptions (again runtime exceptions and errors are still there). You have to have proper (unchecked) exception handling in the right places in your software."
I think you may simply be unfamiliar with when checked exceptions should be used, and when unchecked exceptions should be used.
Unchecked runtime exceptions represent conditions that, generally speaking, reflect errors in your program's logic and cannot be reasonably recovered from at run time; i.e., a bug in your program that will require code changes to fix. Generally speaking, you don't catch unchecked runtime exceptions, you allow them to bubble up to a high level handler. The high level handler will log the error and inform the administrator something went wrong, or display a (possibly localized) message to the user indicating that something went wrong, possibly requesting that they allow an error report to be sent to interested parties, etc.
Checked exceptions represent invalid conditions in areas outside the immediate control of the program (invalid user input, database problems, network outages, absent files). Generally speaking, you handle checked exceptions immediately (or almost immediately) and don't allow them to bubble up to a high level handler. These generally do not represent program bugs that require code fixes, it's just mundane but also important stuff your program logic should handle. By throwing checked exceptions in these cases, you eliminate a lot of burden on the caller to make sure they are handling all reasonable conditions.
"Are you actually properly producing tests for your code before deployment-time ? Otherwise I would not get it why it would be nightmare."
Not every code path is reasonably testable, plus tests are often the first thing to go during a crunch. Despite the best intentions, tests are not a cure-all. For best security, use multiple layers. For best software, use multiple layers. Checked exceptions are one of those layers: help your caller do the right thing, enforced by the compiler itself. Sure, tests are great, too.
"Bloat causes development drag and makes the code more difficult to understand (more scrolling, jumping to different locations in the same file, etc.)."
Since in real code you want to always handle return codes anyway, checked exceptions introduce no unnecessary bloat.
"I think you produce both. I'd compare here checked exceptions to unchecked exceptions like what staticlly typed languages are to dynamically typed languages. And I've seen a lot of developers "loving" statically typed languages because the compiler tells them all their errors (at least they think so)."
At this point I'm fairly certain your misunderstanding comes down to not knowing when and where to use checked exceptions, and when and where to use unchecked exceptions. You're conflating all exceptions into one gigantic indivisible entity and asking, "Why have both?" The answer is: because they're used for different things! I hope I've sufficiently answered that in more detail above.
I'm in favor of static typing over dynamic typing as well, but that's a whole other kettle of fish. However, I'm gratified to see that the wider developer community is finally accepting that static typing can be done in such a fashion that it's just as powerful and expressive as dynamic typing, but without what many consider unnecessary bloat or limitations. Languages will continue to evolve, and I'm confident static typing will win.
All of the above is colored by my application domain (I work on server applications that must run flawlessly for at least weeks, usually months, or even years at a time). Robustness and reliability is at or near the top of my list of requirements for a programming language. Different application domains may trade robustness and reliability for other things, which might make sense for those particular application domains.
Checked exceptions make my life much easier and result in more robust and reliable software, because I simply can't forget to check for things like "file not found" or "connection refused" or "invalid user input" -- my tools makes sure I do that. Thanks, tools!
(However, as a user as well as a developer, I wish all developers put robustness and reliability first.)
The author's example seems fine, provided they can distinguish an exception-from-a-B-transaction vs an exception-from-a-program-bug. If I were him, I'd keep that checkFormat method, but only call it when I catch a RuntimeException, to see if it's something to worry about or not.
I was pleased that the author appears to have tested the performance of both approaches and is making a decision based on real numbers.
javax.xml.ws.WebServiceException is also quite commonly annoying.
Scala's Option type is another example of this - I've watched people be sort of annoyed by it at first, but it tends to really improve the reliability and overall quality of the code when it is used properly.
Or, of course, libraries written by people who just don't like checked exceptions at all.
This article has some more insights I think http://jyops.blogspot.se/2012/03/why-should-you-use-unchecke...