views:

147

answers:

5

Joshua Bloch in his Effective Java writes :

"Use the Javadoc @throws tag to document each unchecked exception that a method can throw, but do not use the throws keyword to include unchecked exceptions in the method declaration. "

Well that sounds reasonable indeed, but how to find out, what unchecked exception can my method throw?

Let's think of a following class :

public class FooClass {

    private MyClass[] myClass;

    /**
     * Creates new FooClass
     */
    public FooClass() { 
        // code omitted
        // do something with myClass
    }

    /**
     * Performs foo operation.<br />
     * Whatever is calculated.
     * @param index Index of a desired element
     * @throws HorribleException When something horrible happens during computation
     */
    public void foo(int index) {
        try {
            myClass[index].doComputation();
        } catch (MyComputationException e) {
            System.out.println("Something horrible happened during computation");
            throw new HorribleException(e);
        }
    }
}

Now, I documented HorribleException, but it is quite obvious, that foo method can also throw unchecked java.lang.ArrayIndexOutOfBoundsException. The more complex the code gets, it's harder to think of all unchecked exceptions that method can throw. My IDE doesn't help me much there and nor does any tool. Since I don't know any tool for this ...

How do you deal with this kind of situation?

A: 

The quote you posted, is just something you have to keep in mind if you want to be the ideal programmer. Programming is not thinking "what can go wrong", but is think how to do something the best way and program it. If it is a personal project, just write what the method does.

However, there are three possible solutions:

  • Do not document the method.
  • Think a minute of what your code does and find out what the most common possible unchecked exceptions could be. Add them to the Java-doc. And if you encounters a new one, find out what the problem is and add it as possible exception.
  • Do not care about the possible exceptions and document only the exceptions you throw in the method body (like: if (obj == null) { throw new NullPointerException(); }).
Martijn Courteaux
+7  A: 

Only document those which you're explicitly throwing yourself or are passing through from another method. The remnant is to be accounted as bugs which are to be fixed by good unit testing and writing solid code.

In this particular case, I'd account ArrayIndexOutOfBoundsException as a bug in your code and I'd fix the code accordingly that it never throws it. I.e. add a check if the array index is in range and handle accordingly by either throwing an exception -which you document- or taking an alternative path.

BalusC
I don't see why the index should be checked. After all, `ArrayIndexOutOfBoundsException` is clear and comprehensible enough. If you check the index manually, you're likely to throw an `ArrayIndexOutofBoundsException` anyway, so I don't see the point in doing it manually. However, I think this one is worth documenting.
Vivien Barousse
@Vivien: I'd rethrow it like as `new IllegalArgumentException("Unknown index")` since it's not obvious to the caller that an array is been used "under the hoods".
BalusC
@Vivien: `ArrayIndexOutOfBoundsException` is perfectly reasonable...if you're dealing with an array. The caller of `foo` isn't; it's dealing with `foo`. The fact that `foo` uses an array under the covers is an implementation detail. So I'd say if `foo` is going to throw an exception, it should throw something more appropriate like `IllegalArgumentException`. But that's only if it's really appropriate for `foo` to throw an exception in this case at *all*. Exceptions are for *exceptional* conditions. Without knowing more about `foo`, we can't tell whether the invalid arg is truly exceptional.
T.J. Crowder
Ok, the example with indexes might not be the best example in this case. What would you do when a method has to take an Object parameter, and the caller gives you a null? Would you write `if (p==null) throw NullPointerException();`? Or, when writing an adapter that parses integers? `catch (NumberFormatException e) { throw new NFException() }`? I think you should let the exception propagate, and just document that in your doc.
Vivien Barousse
@Vivien: Common practice is to only document the ones which you explicitly throws or propagate from the calling method. You also see this practice practically everywhere in Java SE and EE API's. Explicitly throwing NPE like that is also not uncommon. It makes the code self-documenting and more conform the javadoc. I'd however include more detail in the message like the argument name.
BalusC
@BalusC: I basically agree. Especially with core Java API documentation reference. But with all the respect, Vivien is saying the same thing as does Bloch in quote I posted. And that's weird. It's like using Bloch's idiom seems so reasonable, but nearly unreachable. That's why I also prefer TDD and strongly-tested code. I personally don't document unchecked exceptions (if I don't explicitelly throw them) at all.
Xorty
@Xorty: There's still ambiguity in Bloch's words on whether the method is **explicitly** throwing the `RuntimeException` or not. In *this specific case*, if there's no alternative path, I'd throw it as `IllegalArgumentException` since it's not obvious to the caller that an array is been used under the covers. An `ArrayIndexOutOfBoundsException` would give too much implementation detail away and/or cause confusion to starters.
BalusC
A: 

We have a written checkstyle extension that run on our test server. In your sample it would test if HorribleException was documented.

The ArrayIndexOutOfBoundsException will detect with a code review. In your sample code our goals required to throw a InvalidArgumentException instead a ArrayIndexOutOfBoundsException. The documentation of it will be find from test server.

The ArrayIndexOutOfBoundsException can be a warning in FindBugs. But I am not sure.

Horcrux7
A: 

Document only what you're throwing yourself.

In this case, I'd go for a index bounds check and throw my own exception: throw IllegalArgumentException("Index must be smaller than " +myClass.length + ", but is: " + index) and then document the IllegalArgumentException in the JavaDoc.

mhaller
Sure but that's the point. It's hard to think of all possible exceptions - like this one. Now it's quite obvious, but when you have project of many like 500 lines methods and you don't "throw" - means you are not using throw clausule ...
Xorty
@Xorty: If you have many 500-line methods, and if you find it "hard to think of all possible exceptions", those are sure signs that your methods might be too complex, and that you probably need to do a bit of refactoring. See http://c2.com/cgi/wiki?TallerThanMe, and http://c2.com/cgi/wiki?LongMethodSmell .
Grodriguez
@Grodriguez I wish the world was more beauftiful place with more programmers who like unit tests and documentation ... But usually you need to implement some module in already stared project and that's the pain
Xorty
+2  A: 

The more complex the code gets, it's harder to think of all unchecked exceptions that method can throw.

Where you see a problem, I see a very good reason to keep your code simple ;-)

In your exemple, I'd suggest to document the ArrayIndexOutOfBoundsException. Just because that's something someone can have when giving a bad parameter to you method, and it should be written somewhere : "If you given an invalid array index, you'll get an ArrayIndexOutOfBoundsException. For example, the String#charAt() method documents it can throw an IndexOutOfBoundException if the index isn't valid.

In general, you shouldn't document al the exceptions that can arise. You can't predict them all, and you're very likely to forget one. So, document the obvious ones, the one you though of. Try to list the most exceptions as possible, but don't spend to much time on it. After all, if you forget one that should be documented, you can improve your documentation later.

Vivien Barousse
Have you checked the source of `charAt()`? It checks the range and throws it **explicitly** as `StringIndexOutOfBoundsException`. If you would follow the same approach in OP's case, you would throw an `FooIndexOutOfBoundsException`.
BalusC
@BalusC: Ok, that might not be the best example out there. See the comments on your answer.
Vivien Barousse