Javadoc 最佳实践

本文翻译自 Javadoc coding standards - Stephen Colebourne's blog

Javadoc 是 Java 编程中很重要的一部分,然而却很少有人谈论如何去写好一个的 Javadoc。如果想写好 Javdoc,首先最好有一份代码规范。

Javadoc 代码规范

我之前尝试过一些 Javadoc 的标准。考虑到每个人喜好不同,我这里只想谈谈最基本的一些原则,不去涉及方方面面的细节。另外,我们只讨论 Javadoc 的格式,其内容不在本文范围之内。

这里有一份 Oracle 家的指南 要比本文详细的多,不过大部份要求都是一致的。

以下所有条目我都尽可能说的简明,并用一些例子去阐述。

让 Javadoc 像代码一样可读

当你听到 “Javadoc” 这个词的时候,你首先想到的可能是 Javadoc 生成的 HTML 网页,然而实际情况绝非如此。多数情况下,其他人都是在看源代码的时候用到这些 Javadoc,比如你看同事的代码、或是研究第三方库的代码。时刻记住:让 Javadoc 像 Java 代码一样保持可读性。

Public 和 Protected

所有 Public 和 Protected 方法都应当有相应的 Javadoc。Package 和 Private 方法不强求,但是如果有帮助的话加上也很好。

如果子类覆盖了父类中的某个方法,一般来说不需要 Javadoc,除非这个覆盖的实现和原有的差别很大,这时候需要用 Javadoc 说明差异的那部分。@Override 注解不仅标记了方法覆盖,另一方面也是暗示读者要参考原来方法上的文档一起看。

使用标准的 Javadoc 风格注释

Javadoc 以 /** 开头、以 */ 结尾,并且每行要以星号开头:

1
2
3
4
5
6
7
/**
* Standard comment.
*/
public ...

/** Compressed comment. */
public ...

注意别用 **/ 作结尾。

用简单的 HTML tags 就行了,不需要 XHTML

Javadoc 用 HTML tags 来识别段落、列表等等。很多开发者可能觉得 XHTML(HTML 的一种“严格版本”)会更好,其实不然。XHTML 常常会多出一些 tag,这会导致代码变得更复杂了,可读性更差。

此外,Javadoc 的 parser 其实会帮你把没闭合的 tags 自动闭合的,别担心。

用单个 <p> 来分割段落

Javadoc 经常会需要分成好几段。所以问题来了:怎样优雅地加上段落标记?答案是,在两段之间写上一行 <p> 就可以了,不用加 </p> 闭合它。

1
2
3
4
5
6
7
8
9
/**
* First paragraph.
* <p>
* Second paragraph.
* May be on multiple lines.
* <p>
* Third paragraph.
*/
public ...

用单个 <li> 来标记列表项

列表在 Javadoc 中也很常用,比如用来表示一组选项、一些问题等等。推荐的做法是用一个 <li> 作为每项的开头,同样不需要闭合。此外,别忘了加段落 tag:

1
2
3
4
5
6
7
8
9
10
/**
* First paragraph.
* <p><ul>
* <li>the first item
* <li>the second item
* <li>the third item
* </ul><p>
* Second paragraph.
*/
public ...

首句很重要

Javadoc 的首句(用英文句号结束)也被作为这个 Javadoc 的摘要,在折叠的时候只会显示这一句。因此首句必须是个总结性的描述,它最好简洁有力,不能太长。

虽然没有强制要求,我们建议首句自成一个段落,这让代码看起来更清晰。

对于英文注释,推荐使用第三人称来描述,比如 “Gets the foo”、“Sets the bar”、“Consumes the baz”。避免使用第二人称,比如 “Get the foo”。

用 “this” 指代类的对象

当你想描述这个类的一个实例(对象)的时候,用 “this” 来指代它,比如 “Returns a copy of this foo with the bar value updated”

别写太长的句子

尽量让一句话能容纳在一行中,一般来说一行有 80 到 120 个字符。

新的句子就另起一行,这会让代码可读性更好,也会让以后改写 Javadoc 容易很多。

1
2
3
4
5
6
7
8
9
10
/**
* This is the first paragraph, on one line.
* <p>
* This is the first sentence of the second paragraph, on one line.
* This is the second sentence of the second paragraph, on one line.
* This is the third sentence of the second paragraph which is a bit longer so has been
* split onto a second line, as that makes sense.
* This is the fourth sentence, which starts a new line, even though there is space above.
*/
public ...

正确使用 @link@code

很多地方的描述需要涉及到其他类或方法,这时最好用 @link@code

@link 会最终变成一个超链接,它有以下几种形式:

1
2
3
4
5
6
7
8
9
10
/**
* First paragraph.
* <p>
* Link to a class named 'Foo': {@link Foo}.
* Link to a method 'bar' on a class named 'Foo': {@link Foo#bar}.
* Link to a method 'baz' on this class: {@link #baz}.
* Link specifying text of the hyperlink after a space: {@link Foo the Foo class}.
* Link to a method handling method overload {@link Foo#bar(String,int)}.
*/
public ...

@code 用来标记一小段等宽字体,也可以用来标记某个类或方法,但不会生成超链接。

建议在第一次提到某个类或方法的时候用 @link,此后直接用 @code 即可。

不要在首句中使用 @link

之前提到,Javadoc 的首句也被用作概要,首句中的超链接会让读者感到混乱。如果一定要在第一句话中引用其它类或方法,始终用 @code 而不是 @link,第二句开始再用 @link

null、true、false 不必用 @code 标记

null、true、false 这些词在 Javadoc 中太常用了,如果每次都加上 @code,无论是对读者还是作者都是个负担。

使用 @param@return@throws

几乎所有方法都会输入几个参数、输出一个结果,@param@return 就是用来描述这些输入输出参数的,@throws 用于描述方法抛出的异常。

如果有多个输入参数,@param 的顺序也要和参数一致。@return 应当始终放在 @param 之后,然后才是 @throws

为范型参数加上 @param

如果一个类或方法有范型参数(例如 <T>),这些参数也应当被文档化,推荐的做法是给 <T> 也加上一个 @param 说明。

@param 之前空一行

始终在 Javadoc 的内容和 @param@return 之间留个空行,这让代码的可读性更佳。

用短语来描述 @param@return

@param@return 后面跟的的描述是个短语,而非完整的句子,因此它得用小写字母开头(经常是 the),结尾也不需要用句号。

用 if-句来描述 @throws

@throws 通常跟着一个 “if” 句子来描述抛异常的情形,比如 “@throws IllegalArgumentException if the file could not be found”。

@param 的参数名之后空两格

在源代码中阅读 Javadoc 的时候,如果参数名后面只有一个空格,读起来会有点困难,两个空格就好很多。另外,避免把参数按列对齐,否则参数改名、增减参数的时候会很麻烦。

1
2
3
4
5
6
7
8
/**
* Javadoc text.
*
* @param foo the foo parameter
* @param bar the bar parameter
* @return the baz content
*/
public String process(String foo, String bar) {...}

写明各参数和返回值的 null 行为

一个方法是否接受 null、会不会返回 null 对于其他开发者是十分重要的信息。除非是原始类型,@param@return 都应该注明它是否接受或返回 null。以下标准若适用请务必遵循:

  • “not null” 表明不接受 null,若输入 null 可能导致异常,例如 NullPointerException
  • “may be null” 表明可以传入 null 参数
  • “null treated as xxx” 表明 null 值等价于某个值
  • “null returns xxx” 表明如果输入 null 则一定会返回某个值

定义清楚这些之后,要再为 NullPointerException 写 @throws

1
2
3
4
5
6
7
8
/**
* Javadoc text.
*
* @param foo the foo parameter, not null
* @param bar the bar parameter, null returns null
* @return the baz content, null if not processed
*/
public String process(String foo, String bar) {...}

有人可能想在某个地方(像是类或包的 Javadoc)集中定义 null 相关行为,但我们不建议你这么做,因为这对别人并没有帮助。方法上的 Javadoc 很容易就能看到,而类或包层级的 Javadoc 要去翻一遍才能找到。

其他简单的约束条件也建议写到 Javadoc 里,比如 “not empty, not null”。原始类型也可以加上边界约束,比如 “from 1 to 5” 或 “not negative”

给 Specification 加上 implementation notes

如果某个接口允许第三方来实现,而你为这个接口写了个正式的规格说明(specification),这时候考虑加个 “implementation notes” 章节。这通常出现在类的 Javadoc 上,用于描述一些不太好写在特定方法上的东西,或者一些其他人不感兴趣的东西。参考这个例子

不要用 @author

@author 用来标记类的作者,这个功能已经过时了,不要用。版本控制系统(例如 git)会记住作者的。

例子

这个 ThreeTen 项目里有一些更完整的例子

总结

希望这些建议能帮你写出更好的 Javadoc。当然,这只是一份建议,你也可以选择其他标准来参考。