The more (Java) code I write, the more I dislike checked exceptions. In the last weeks, I wrote a lot methods which needed to catch an exception which can never occur. But checked exceptions were introduced for a reason, so I wondered how I would improve exceptions in my own ideal Java.
Examples of nasty checked exceptions
I wrote a builder for generating soap request with Javas SOAP API (
javax.xml.soap). It seems that this API throws an
SOAPException in each method. Of course I implemented the builder in a way that the users could not create invalid requests. Every time a SOAPException occurs it is definitely a bug in the builder. Of course I don’t throw the SOAPException in my API, I don’t want my user’s to handle them. But I need to invest some time to make my builder code readable although I had to catch a lot of SOAPExceptions.
In another example I searched a class annotated with a specific annotation and a specific annotation value. In the code I needed to use
Class.forName(classForAnnotationValue). This throws an
ClassNotFoundException. But I just found the class in the classpath! Afterwards I call
.newInstance() also throwing two exceptions (InstantiationException and IllegalAccessException).
A good reason for checked exceptions
Of course there are good reasons why checked exceptions were introduced. Developers are forced to think about typical exceptional behaviour. It is possible that you try to create an instance of an interface or an abstract class, which is not possible. Or that you try to instantiate an object of a class only having a private constructor. Of course in a lot of cases, searching a class for a string will return no result and therefore it is very good to force the programmer to think about how his program should react if there is no class for the given string. And let’s be honest, who would read the JavaDoc and check which runtime exceptions are thrown and would decide which he should handle and which not? (Maybe we would do, if checked exceptions had never exist..) But if you know that the checked exception you are caching will never occur, it is just annoying to have the boiler plate try-catch-block.
So what to do instead
In my own ideal Java I would have a ‘sometimes checked exceptions’ concept. Sometimes checked exceptions are checked exceptions which you can ignore (similar to PMD rules). If you call a method throwing a checked exception, you would get a compiler warning (a warning, not a compiler error) and of course your IDE would mark the method call. There are two possibilities to handle this warning:
- Just use the known try-catch-block
- Ignore the exception explicitly (e.g. by an annotation, similar to
I’m not quite sure which is the most clever way to suppress the warning. Maybe an annotation is not the best way, but obviously a way that will work. If the exception is suppressed and occurs against expectation it is thrown as a runtime exception.
This concept is not finished yet. Maybe there is something I miss, but it feels good and it would reduce a lot of boiler plate code. The code would not only be shorter and cleaner, but also more easy to understand.
I am sure that I am not the first person having such an idea. If you know such a concept or somebody having the same idea, please let me know! I am also very curious what you think about it. Are there any situations where this won’t work?
“Developers are forced to think about typical exceptional behaviour. ”
Nope. They’re forced to hit alt-enter and have it do a printStackTrace(). This is why I hate checked exceptions- people are lazy and don’t handle them properly, leading to bugs.
I’m kind-of ok with checked exceptions on APIs, as they form part of the contract, which I think is what you’re getting at. If I understand correctly, sometimes-checked-exception is basically a way to say “don’t make me catch this exception, because I know for certain I won’t hit it”. I would like this too, but mainly because I hate checked exceptions and the boilerplate code that it forces on me. Even better, I’d like to have it auto-wrap any exception that does come up in a RuntimeException so that, if somehow I was wrong and an error does happen, I can deal with it higher up.
Either way, this is just a solution to get around the existence of checked exceptions. C# doesn’t have them, lucky them.
I don’t think checked exceptions were ever a good idea. C# only has unchecked exceptions for a reason. And the Java industry is moving away from them.
Checked exceptions in Java are nothing but syntatic sugar, the JVM or runtime doesn’t know about them, only the compiler. You should think of them as a static analysis tool. Other JVM languages are therefore free to not include them, and they almost never do.
The sole use of checked exceptions is when a method causes an exception that demands an alternative response from the caller, which expects these conditions and has a strategy for coping with them.
In every other situation the caller can do nothing else but either shallow the exception (the bad choice), let the exception to propagate up the call stack untouched (can be a bad choice) or wrap and rethrow the exception (exception chaining, the good choice).
Checked exception make it harder for the exception to propagate up, because the caller methods must be annotated with the exception, which in turn forces the programmer to think about the exception from the callee and whether his caller method can do anything to handle it. Seems like a wonderful thing and it is.
Sadly checked exceptions take one set of problems and trade them for another set of problems, which usually makes them not desirable even when they otherwise make perfect sense. But they rarely make sense, because 9 times out of 10 you the direct or indirect caller cannot handle the exception. What you usually have instead is a single, centralized exception handler that caches ’em all. The handler logs the exceptions and cleans up after them to keep a consistent state.
The main problem with checked exceptions is that they make it harder for your method to change. Once you declare that a method throws a certain exception that becomes part of the API and a public API is immutable. With checked exceptions when the method changes what exception it throws all client code is suddenly broken. With unchecked exceptions that can also happen, but tend to happen not as much, because the caller is not forced to declare precisely the exceptions it propagates and the programmers rarely care about handling exceptions.
The reason why we use object-oriented programming techniques is to be able to handle the inevitable change. We do that by striving for sets of loosely coupled and highly coherent components bearing a single responsibility.
Checked exceptions tend to make your system too rigid. They introduce an unjustified dependency between a method and all of it’s direct and indirect callers.
Another issue is that programmers tend to be human too and to avoid the constant nagging of the compiler they sometimes shallow checked exceptions, if only temporarily. Then of course the the whole thing remains unnoticed an uncorrected for years.
Yet another issue with checked exceptions is that they destroy scalability. If your subsystems declare 10 checked exceptions and you have 4 subsystems suddenly you have to declare 40 checked exceptions. In large systems they can become quite unwieldy even with proper exception chaining and base exception types that can be subclassed.
Checked exceptions also tend to break generic and functional programming. Using generics you can abstract over method return types and argument types, but abstracting over types of checked exceptions that can be thrown leads to boilerplate code and caching an Exception.
The reason is because a method can be declared to throw zero or more exception type, but generic type parameters must represent exactly one type. So you are forced to generify over a set of exception types.
Checked exceptions in Java don’t play nice with the rest of the language as you can see and the same is true for functional programming in Java 8 (functional interfaces, lambda, streams). With checked exceptions the result is boilerplate and workarounds such as sneakily throwing checked exceptions, which abuses the language into throwing a checked exception without the need to declare it.
To fix these issues Java would need to provide a better way to propagate checked exceptions, but it seems that the problem was simply ignored by Oracle.
So I think that there are plenty of reasons to avoid using checked exceptions. Smaller projects might be able to get away with it, but since Java 5 introduced generics and Java 8 functional programming checked exceptions are kind of broken. I don’t even think they should be repaired, I think we should just forget about them and come up with better static analysis tools.
Hey, I just found your blog. I love people who are enthusiastic about their craft.
I think you’re heading down the right path and you are right, the goal should be mastery, even if we all know that what trully matters is the journey itself.
I also like your attitude about being a professional and being responsible.
I am going to go ahead and say that you kind of fit the description of a perfectionist. I know the signs because I happen to be one. There are healthy perfectionists too, so I am not saying that it’s a bad thing, it is for me though, but I am trying to adapt to it.
I know, I know, this is a personal thing, but I wonder if I am right 🙂 Do you think you are a perfectionist? See Perfectionism.
So about software development, here is the thing. I don’t think experience should be measured by the number of years that passed since someone first started developing software.
And I don’t think having just passive knowledge is that useful.
I am talking out of my ass here, I am inexperienced and yet to walk the walk, and unlike you I don’t have education and nobody would call me even a junior developer, but like you I’m also struggling to become a better developer.
I think a great receipt against forgetting knowledge is to keep coding and keep reading about the subject as much as you can.
The great thing about experimenting is that you can experience direcly the usefulness of concepts such as SOLID and that way you can understand them better and argue about them better.
Learning is hard. And after a while the whole romantic cloud about it disperses and what you realize the fact that you have to give it a lot of your time and energy. Simple daily disciplines, repeated consistently over time. And you hope that your motivation can keep you floating.
For me the hardest part is to keep failing. I have an unnatural amount of fear about that, but it’s part of the process. Failing fast is the second thing for me that is hard to learn, but it is very important for not only a software, but for a business and even for your career. Failing fast is important because you want to know if there is a problem as soon as it occurs. The sooner you realize the problem the sooner you can solve it.
I almost left out one important detail, well not important for me, but we are talking about the software industry. You are a woman. That means you have to try twice as hard and people won’t readily acknowledge that you are qualified, unless you show them every day that you are.
This whole thing sucks, and the industry happens to suffer a lot because of it, but you know who needs a better world and a better industry when we can just keep making your lives harder so we can feel better about ourselves.