13

It is often the case that a method imposes constraints on its arguments that cannot be described by the type system. For example, a method might require that some argument be non-null, or some int-typed argument be positive. There might also be more complex preconditions, for example that a certain method was called before, or that a certain object is in some state. What is the best way to document this in Javadoc?

For example, suppose I have the following public library function, where the argument cannot be negative:

public void foo(int bar) {
    if (bar < 0) {
        throw new IllegalArgumentException("Negative bars cannot be food.");
    }
    ...
} 

I want to document this in such a way that it "stands out" from the rest of the documentation text so that documentation readers know immediately where they have to look. Currently, I do this by adding throws clauses to the Javadoc:

/**
 * Foos a bar.
 * @param bar  the bar to be food
 * @throws IllegalArgumentException  if bar is negative
 */
public void foo(int bar) {
    ...

However, this introduces the throwing of the exception into the specification of the method. Now, library users might depend on this behavior in their code. So if I want to change the method in a next version in such a way that also negative parameters are allowed, I might break clients' code.

Are there any best practices to document things like this in Javadoc? I have thought of:

  • Describing in the documentation text that the method has undefined behavior if the argument is negative. However, this does not really stand out, so it might be missed by a lot of library users.
  • Using annotations (public void foo(@NonNegative int bar)). However, for this a standard set of annotations would be ideal, and this standard set does not appear to exist.
Hoopje
  • 12,677
  • 8
  • 34
  • 50
  • One rule when speaking in public is to "tell them three times." I don't see any problem repeating yourself, and I do it myself in Java docs. Just say "bar must be equal or greater than zero" in the text. I don't know if it's best practice, but I consider it helpful to someone trying to read the doc. – markspace Nov 03 '14 at 22:56
  • You can put as much information in the Javadoc as you think is necessary. Instead of documenting "what the code does" you should try to explain "why the code is needed" and "how it should be used." I typically document preconditions by including a brief explanation of under what circumstances (preconditions) a method should be called. Also as important, document if the method returns null and under what conditions (Post-conditions). – hfontanez Nov 03 '14 at 23:05
  • I think it is always good to have as little non-statically checked contracts as possible, e.g., use Java 8's or Guava's `Optional` type instead of returning or accepting `null`. If you do have more complex contracts however, and want to specify them as Pre- and Postconditions, you may want to search for an appropriate library that supports Javadoc. I only know of a project named JASS: http://csd.informatik.uni-oldenburg.de/~jass/ but I am not sure how up-to-date it is. – Kulu Limpa Nov 03 '14 at 23:14
  • 1
    You can use JSR 305 annotations to cover some of it (you can annotate parameter arguments as `@Nonnull` for instance); you can even create your own `@Documented` annotations to cover preconditions not covered by existing ones. Note also that for JSR 305, FindBugs has an implementation which is actually used in the program itself (since these annotations have a class retention policy) – fge Nov 03 '14 at 23:29

2 Answers2

6

You seem hesitant to rely on your API's Javadocs to provide exactly that: documentation for your API. While I agree some developers will invariably ignore its warnings, I think historically Javadocs have been entirely adequate in providing sufficient guidance on how to correctly use an API. You can go crazy creating all kinds of custom Annotations, but in the end people will still implement your API incorrectly at times.

If you did want to go even further beyond what you already have there, you could also implement self-documenting naming conventions wherever practical. For example:

/**
 * Foos a positive bar.
 * @param positiveBar  the non-zero,non-negative bar to be food
 * @throws IllegalArgumentException  if bar is zero or negative
 */
public void foo(int positiveBar) {
    ...

Lastly, although your question is about how to document these constraints and not handle them, I will say to not underestimate the value of IllegalArgumentException. This is precisely what it should be used for and engineers should not be afraid to throw this exception to indicate a programming error. Developers aren't going to get far without reading the docs when their implementation doesn't run.

Jeffrey Mixon
  • 12,846
  • 4
  • 32
  • 55
  • 1
    Added, never forget to use proper encapsulation... Alot can be said for strong names on your methods. Many comments can be avoided by simply doing public void fooPostiveBar(int bar). Also, your exception does not need to be runtime if you expect the user to actually handle this edge case. By changing the exception to NonNegativeBarException extends Exception – AnthonyJClink Nov 04 '14 at 00:28
3

You can create custom javadoc tags, i.e. @pre @inv and @post for precondition, invariant and postcondition.

Further, Joshua Bloch argues in Effective Java Second Edition:

The doc comment should enumerate all of the method's preconditions, which are the things that have to be true in order for a client to invoke it, and its postconditions Typically, preconditions are described implicitly by the @throws tags for unchecked exceptions; each unchecked exception corresponds to a precondition violation. Also, preconditions can be specified along with the affected parameters in their @param tags.

Examples:

/**
 * @param index index of element to return; must be non-negative and less
 *              than the size of this list
 * @throws IndexOutOfBoundsException if the index is out of range
 *                                   ({@code index < 0 || index >= this.size()})
 */

Note, that every exceptions begins with if, followed by a clause describing the conditions under which the exception is thrown. (precondition) This is often described with arithmetic expressions.

Brian Rodriguez
  • 4,250
  • 1
  • 16
  • 37
Matthias Holdorf
  • 1,040
  • 1
  • 14
  • 19
  • 1
    It's all well and good saying what the Javadocs *should* do but what coders can *readily* document under the current set of tags and effects is not enough, I feel. Why aren't there official *@pre*, *@post* and *@inv* tags for methods in Javadoc ? – Trunk Aug 05 '19 at 19:08