Tuesday, September 16, 2008

Why java.io.PrintStream is badly designed

Sweeping the exceptions under the carpet is considered a bad practice, I mean really bad practice. Consider, for instance,
try {
  do something
} catch (SomeException e) {
  set a flag (or, even worse, do nothing!)
}

Obviously, one of the great advantages of having an exception mechanism is that programmers do not need to explicitly check for error conditions after every single statement: when something wrong happens, an exception is thrown and it will be handled where it is sensible to do so (low-level code can't usually know what to do but, thanks to the automatic propagation of exceptions, it luckily doesn't have to).

When exceptions are swept under the carpet, instead, higher level-code can't react with some sensible action (when something wrong happens) for the simple reason that such a code has no way to know that something has happened! Well, ok, if a flag is set, then the high-level code could do something if it checks for the flag after every single statement. This works, but it clutters the code with a series of "if (flag is set) do something appropriate" that could be totally avoided by simply using the exception mechanism in the first place.

I hope everyone agrees that sweeping exceptions sucks; keeping this in mind, let's read the documentation of java.io.PrintStream (from the API docs): Two other features are provided as well. Unlike other output streams, a PrintStream never throws an IOException; instead, exceptional situations merely set an internal flag that can be tested via the checkError method. Ah ah! They call it a feature! I'm so sorry I've missed the point and I thought it's a bad hack to avoid to follow their "Catch or Specify requirement".

I know: it would be annoying to declare that every method that prints something may throw java.io.IOException, yet, it would be the only correct way to handle the error condition. With a flag we:

  1. have to check the flag everytime
  2. lose both the reason (the type of the exception) and the place (the line of code) where something bad has happened

Is this the worst thing about the class PrintStream? Actually, no, it isn't: PrintStream extends Outpustream, overriding the write methods with its set-the-flag semantics. So, every time you handle an OutputStream you're handling something that may or may not throws exceptions in the way it should!

Sun has admirably implemented two "features" ;) in one fell swoop:

  • sweeping the exceptions under the carpet (bad practice)
  • defining a subtype whose methods do not respect the specification of its supertype, that clearly states that an exception is thrown when an I/O error occurs (awful practice)

No comments: