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.
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 Annotation
s, 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.
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.