Java exception hierarchy, what's the point?

I find myself busy refactoring my pet project and removing all the noise associated with exception exception declarations. In principle, everything that violates the condition is a violation of the statement or, formally, an AssertionError, which in Java is generously allowed to be excluded from the method signature. My question is: what is the meaning of the hierarchy of Exceptions? My experience is that each exception is unique, and there are no formal criteria to establish that one set of exceptions is a subset of the other. Even the distinction between checked and unchecked exceptions is fuzzy, why, for example, would I insist on throwing a client code exception when a lazy (or impatient) programmer can easily wrap it in a RuntimeException and restore it to me?

+4
source share
3 answers

I don’t think the hierarchy is bad, it’s just not very useful. 99% of the time, an exception indicates that something very bad happened, and my options are few. We are mostly dead. The only choice is to select the appropriate error message to display to users.

I am one of those lazy programmers. If I call the processFile () method from the bowels of my code, and this throws an exception, I will probably formulate the message “Your file cannot be processed” and will throw a RuntimeException. We cannot recover. We are a former program. No more. Nothing can be achieved there in breaking up the code by binding the checked exception to each method in the call stack.

Invariably, I encode something like this:

try { processAFile(); } catch (Exception e) { // Just catch them all. logger.error(e); logger.error("log any important information here."); throw new RuntimeException("We were unable to process your file."); } 

Currently, a RuntimeException comes to the main method and is handled responsibly.

At the top of the code, I catch all exceptions, register, if necessary, usually roll back the transaction and do what is necessary to display an error message that may be useful to the user.

I like the distinction between checked and unchecked exceptions, although I cannot say why, because I think about it. IntelliJ will automatically populate several catch catch blocks. I think, “hmm, that’s interesting,” and replace them with a single catch (Exception e) , since the recovery is always the same. In the above example, I have to check and not check, then I am dead, and the error message is the same one who cares. Why throw a checked exception. The dead are dead.

The only time I can think about where I handle certain exceptions is when I call a method that throws an exception incorrectly instead of returning an error condition.

+1
source

In theory, the Java exception hierarchy makes some sense:

 Throwable* -> Error (OutOfMemoryError, etc.) -> Exception (IOException, SQLException, etc.) -> RuntimeException (IndexOutOfBoundsException, NullPointer, etc.) 

At present, the theory underlying this does have some meaning. (Actual implementation leaves much to be desired due to congestion, sad.)

Error -descended Throwable objects are serious errors due to which it is expected that the program will not be able to recover. (As a rule, you do not understand this, in other words.) One of these statements is a serious general systemic problem. For example, when you run out of memory, this is a serious setback somewhere, because in theory the GC was already desperate to make room for you. Catching this is pointless.

Exception -descended Throwable objects are all errors that can reasonably be expected during a normal operation; such as network errors, file system errors, etc. Indeed, with the exception of those that came from RuntimeException , it is required that programs explicitly handle these errors - these are the so-called "checked exceptions". (Of course, bad programmers will "cope" with them by deleting them, but this is a programmer’s problem, not a system problem.)

RuntimeException -descended Throwable objects are slightly different. These are errors that should not be expected, but which the program could reasonably recover after they occur. Therefore, they are not checked (programs are not required to process them), but they can be, if there are reasonable ways to cope with the situation. In the general case, these exceptions are some kind of software error (unlike the previous category, which are expected errors that occur during normal operation).

So, does this hierarchy make sense? Well, at some level it seems. Error thrown by the system and is a serious error, which is probably going to kill your program. RuntimeException used properly, a RuntimeException is thrown by system libraries (or occasionally by your own program) and usually means that someone is screwed somewhere, but this is normal because you can recover it. Other Exception objects are expected errors, which are actually part of the specified interface of your objects.

But...

This last item is a problem. Verified exceptions are, quite frankly, a serious pain in the lower body anatomy. They uselessly clutter up the code with the exception handling template in such a way that, in my opinion, render (and many others, I could add!), The whole point of exceptions: the separation of error detection and error handling. Forcing each method in the chain to handle the & mdash exception, even if it just overwrites it and passes it! - the code clutters the little things of error handling to such an extent that it is slightly better than returning status codes and processing them after each method call.

If Java were a smarter programming language, checked exceptions would be checked at compile time / reference to see if they are handled correctly at the system scale, and not when each method is called in every class file. Unfortunately, the whole Java architecture does not allow this level of analysis of the entire program, and the result, again, in my opinion (but again shared by many), is actually a mixture of the worst two worlds handling exceptions and returning errors: you get most of the boilerplate forests with obvious errors, but you also get COME FROM exception behavior.

+11
source

“My experience is that each exception is unique and there are no formal criteria to establish that one set of exceptions is a subset of the other. Even the difference between checked and unchecked exceptions is fuzzy, why, for example, will I insist on client code exception when a lazy (or impatient) programmer can easily wrap it in a RuntimeException and retrieve it on me? "

All this is correct.

As Joel Spolsky once said: "Design is the art of choice." And thus, designing an exception hierarchy involves a choice. Sometimes they work pretty well, sometimes they don't.

One of the advantages of [having an exception hierarchy], I see that by catching a supertype exception, you will catch a number of exception types with a single catch clause. Is this always what the user wants? Absolutely not. Can the user avoid this in those (and only those) cases when this is not what he wants? Definitely yes. Is it an alternative to not have supertypes exceptions? Imo definitely not. Take a look at the IOException hierarchy and imagine that you need to write a catch clause for each of your children in every place where they can occur ...

Even with all the possible flaws that are pretty well known by now, I still think that the Java exception handling mechanism is superior to any other I have ever seen.

+3
source

All Articles