Dienstag, 29. Juni 2010

Simpler Java #2 Know your RuntimeExceptions

The most common part that tends to go wrong in java is the exception handling. (Someone there how has never used an empty (or just "log") catch block. Anyone??).

To avoid the common problem, here are some very basic guidelines which should help:

Checked vs. RuntimeExceptions
  • Use checked exceptions for problems which MAY occur and which are not in scope of the app to handle (io-errors, db-errors). These must be handled in a suitable way, usually you must also inform the user about these problems.
  • Use unchecked exceptions whenever a problem occurs which cannot be handled in any way. The app might handle it on the highest level (users don't like GUI's which crash into nirvana), but in general the programmer has made a serious mistake here which must be solved in the code.
Exception Wrapping
Sometimes, you encounter a checked exception for something you know which must never go wrong in your app and which you do not want to handle therefore. An example are io-errors on stream.close() or checked exceptions on reflection or cloning (the last ones are flaws in the jdk design. ClassNotFound is not a checked exception by its definition. Usually, you can never handle this one).

In these cases, do this:
try{
  // something that throws classnotfound
}catch(ClassNotFoundException e){
  throw new RuntimeException(e);
  // throw new org.apache.commons.lang.UnhandledException(e);
}

This wraps your checked exception in a runtime exception.

Do not return null
I said it in the last post, and I say it again: Null is not an appropiate return value if something went wrong. Use either a checked exception if an outer condition fails OR an unchecked one (more common) if the programmer made a mistake. Never return null just if something went wrong.
Null is reserved for lookup with maps and the like in java (and even thats crappy), and for nothing else.

Returning null has one good point, though: Its easy to wage the quality of source code simply by counting how often null is used, and why. ;-)


JDK- Unchecked Exceptions
There are some JDK-Exceptions which should be used in some of the most common idioms in java programming:
IllegalArgumentException: Most common exception ever. Use this for all kinds of invalid inputs, like null values  (DONT use NoPE here!)
UncheckedOperationException: Used mostly for interfaces which allow mutation (java.util.List) but the concrete implementation does NOT (unmodifiable List).
IllegalStateException: Throw this one if your objects have a certain order in which their operations must be done, and this order has been broken.
IndexOutOfBounds: Very seldom should your implement your own indices. If you do, however, then this is an appropiate exceptions.

Why you should try to avoid using unchecked exceptions
Last paragraph told you HOW to use unchecked exception. Now I tell you the following: The worse your API design, the more you need to deal with these and the more you need to rely on Runtime safety rather than compile time saftey. UncheckedExceptions report problems which should not have been happened. The better your ApplicationDesign, the more of these can be converted into Compiletime problems.

Some tipps:
  1. Don't use interfaces which throw UnsupportedOperationExceptions.. Make smaller interfaces instead and use inheritance! (Most common example: ReadInterface, WriteInterface extends ReadInterface). The JDK-Collections seriously got this one wrong!
  2. Avoid Objects with complex state - and thereby the IllegalStateException. Use Immutables (more on this one later.)
  3. Use IllegalArgumentExceptions heavily! IllegalArgs make your program more robust, and they are easy to avoid and should not occur at runtime. Throwing these early can save you great deal of work instead of searching for the reason why a NoPE or something occured!
  4. Always use the exception if you have to. The last points told you HOW to avoid the situations. If you, however have a lot of states, you must throw that IllegalStateException or everything gets much worse. However, the sole fact that you have to do this should at least make you think about your API design.
  5. Avoid Indexing on Collections. No Index Access -> No IndexOutBounds. Seems logical, right? Try to avoid indexing at all cost. Most of the time, indexing can be done simply better by designing specialised classes (instead of given each index position in a list or an array its own meaning), and by using the extended for loop. Starting from JDK 1.5, Lists should never ever be looped by their indices - arrays should be avoided anyway, exception for lightweight parameters.

Keine Kommentare:

Kommentar veröffentlichen