メソッドのJavadocには何を書くべきなのか

Javaで開発しているチームで、Javadocに何を書くべきかわからない、という課題がありました。[エンジニアのためのJavadoc再入門講座](https://www.shoeisha.co.jp/book/detail/9784798119489)の3章を読んで自分なりにまとめた内容となります。


## メソッドのJavadocの構成
メソッドのJavadocの構成の基本形は以下となります。

``` java
/**
 * 主説明
 * @param 引数の説明
 * @return 返却値の説明
 * @throws 例外の説明
 */
```

## 引数の説明に書くべきこと
まず、引数の説明に書くべきことについて。主説明については後回しにします。
引数を取るメソッドのJavadocにはすべての引数についての説明を記載すべきです。引数の説明には`@param`タグを使用します。
引数の定義域が大事なポイントになります。引数に取りうる値の範囲は明確になっているかを最初に確認しましょう。[^1]

### 特別な値を受け取ったときの挙動
nullや0、負数、空文字などの特別な値を受け取ることができるかを書いておくべきでしょう。受け取れる場合に特殊な挙動となる場合はそれについても記載が必要です。

### 定義領域外の値を受け取ったときの挙動
定義域外の値を受け取ったときに、メソッドが(例外スロー以外の)何らかの責務を果たす場合は、その挙動について記載が必要です。

``` java
// 悪い例
/**
 * @param purchases 追加する購入数。
 */

// 良い例
/**
 * @param purchases 追加する購入数。正の整数を指定できる。負数が指定された場合、購入のキャンセル数となる。
 */
```
なお、メソッドが何の責務も果たさずに例外をスローするだけの場合は「例外の説明」に記載を行うのがふさわしい場合もあります。

### 受け取ったオブジェクトの状態は変更されるか
引数として受け取ったオブジェクトの状態がメソッドの前後で変更される(=副作用がある)場合は、引数の説明に記載することで呼び出し側に注意喚起する必要があります。[^2]

``` java
// 悪い例
/**
 * @param list ソート対象のリスト。
 */

// 良い例
/**
 * @param list ソート対象のリスト。リストの要素は直接変更される。
 */
```

### 定義域内のすべての値に対して返却値が定義されているか
定義域内とされている値に対して返却値があるかを確認します。もし適切な返却値が存在しない値があるのであれば、それは定義域外としてJavadocに記載が必要です。定義域内とされている値に穴がないかも確認しておきましょう。

``` java
// 悪い例
/**
 * @param amount 発注数。整数を指定する。
 */

// 良い例
/**
 * @param amount 発注数。1以上の整数を指定する。
 */
```

## 返却値の説明に書くべきこと
返却値があるメソッドにはJavadocに返却値の説明を記載すべきです。返却値の説明には`@return`タグを使用します。

### 引数に定義域外の値を受け取ったとき、何らかの値を返却する場合
原則として、定義域外の引数を受け取ったとき、メソッドの責務は無いため例外を返却して終了するべきです。ですが、本来の定義域外の値に対しても返却値を定義しなければならない状況もあります。その場合は、そのことを返却値の説明に記載すべきです。

例えば、[java.lang.Math](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/Math.html)パッケージの`sqrt()`メソッドは、引数の平方根を求めるメソッドです。本来は負の数は定義域外となりますが、負の数を受け取るときはNaNを返す実装となっています。Javadocの戻り値の説明は`aの正の平方根。引数がNaNであるかゼロよりも小さい場合は、結果もNaN。`となっており、定義域外の値に対する戻り値がわかりやすく記載されています。

### メソッドの呼び出し元で気をつける必要のある値を返す場合
「特定の条件のときnullを返す」、「エラー発生時はエラーコードを負の値で返す」など、呼び出し元で特別な配慮や検査が必要となるような値を返す場合は、そのことについての記載があるべきです。

``` java
// 悪い例
/**
 * @return 条件にマッチしたオブジェクト。
 */

// 良い例
/**
 * @return 条件にマッチしたオブジェクト。どれにもマッチしなかった場合はnullを返す。
 */
```

### 特定の値に対して特別な意味を与えている場合
メソッドの処理結果を返却値によって示すような場合はどの返却値がどの意味を持つのかを記載すべきです。例えば[`Comparable.compareTo()`](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/Comparable.html#compareTo-T-)は、引数に指定されたオブジェクトより小さい場合は負の整数、等しい場合は`0`、大きい場合は正の整数を返却する挙動となります。各返却値の情報が記載されていないと呼び出し元で判断することができなくなります。

``` java
// 悪い例
/**
 * @return このオブジェクトと指定されたオブジェクトの比較結果の整数値
 */

// 良い例
/**
 * @return このオブジェクトが指定されたオブジェクトより小さい場合は負の整数、等しい場合はゼロ、大きい場合は正の整数
 */
```

### 変更可能なオブジェクトを返却するが、変更すると問題がある場合
大前提で返却値のオブジェクトが変更可能（ミュータブル）な状態で返却される場合、そのオブジェクトが変更されても問題ないように設計するべきです。しかし、何らかの理由で変更されると困る場合は、そのことをJavadocに記載すべきです。

### ソート済みのコレクションや配列を返却する場合
返却するコレクションや配列がソートされている場合、ソート順を記載すべきです。記載がないと呼び出し元で二重にソートをかけてしまい、無駄になります。

``` java
// 悪い例
/**
 * @return ユーザーのリスト
 */

// 良い例
/**
 * @return IDの昇順でソートされたユーザーのリスト
 */
```

## 例外の説明に記載すべきこと
例外を発生させるメソッドにはJavadocに例外の説明を記載できます。ただし、そのメソッドから発生するすべての例外を書くことはできません。必要十分な例外についてJavadocに記載する必要があります。例外の説明には`@throws`タグを使用します。

### 例外の発生する状況
特定の理由から例外が発生するような事態になるとすれば、その状況や原因についての説明が必要です。なお、定義域外の引数が渡されたときに例外を発生させるような挙動の場合、その条件は例外の説明に記載し、引数の説明からは省く方が望ましいです。両方に記載すると内容が重複するため、内容が変わったときに両方の修正が必要となります。

``` java
// 悪い例
/**
 * @param width 横幅の長さ。0以下の値は認められない
 * @throws IllegalArgumentException 横幅の長さに0以下の値が指定された場合
 */

// 良い例
/**
 * @param width 横幅の長さ
 * @throws IllegalArgumentException 横幅の長さに0以下の値が指定された場合
 */
```

### 例外の回避方法
例外の説明を読んだときに、そのメソッドを使用する人が例外の回避方法を把握できる記述になっているかを確認しましょう。発生条件に曖昧な表現が含まれていると、呼び出し元での対応が困難になる場合があります。

``` java
// 悪い例
/**
 * @throws IllegalStateException オブジェクトの状態が不正だった場合
 */

// 良い例
/**
 * @throws IllegalStateException このオブジェクトの縦幅、横幅のいずれかに0以下の値が設定されている場合
 */
```

### 呼び出し元で対応可能な非検査例外の説明を記載する
Javaではそのメソッドが発生させうるすべての非検査例外を記載することが事実上不可能です。そのため、すべての非検査例外をJavadocに記載する必要はありません。しかし、呼び出し元での対応が可能な例外については記載を行うべきです。例えばSpring Frameworkではデータアクセス時の問題を`DataAccessException`として通知します。この例外をハンドリングするか、ハンドリングしないかはそれを使う開発者が選択できるようになっています。このように、呼び出し元でのハンドリングの有無を選択できるような例外については、Javadocに説明を記載すべきと言えます。

## メソッドの主説明に記述すべきこと
メソッドの主説明には**引数/返却値/例外の説明で表現しきれない情報**を記載します。逆に言うと、引数/返却値/例外の説明がしっかり書けていれば主説明に記載する内容は少なくなります。

### メソッドが何を行うものか
そのメソッドは何を行うのか、どんな理由で存在しているのかの記載は主説明に書くべきです。

### メソッドを呼び出す前提条件
メソッドを呼び出す前にしておかなければならないことや、呼び出せないような局面がある場合は、それについての記載が必要です。

### メソッドを呼び出すことよって副作用が発生する場合
メソッドを呼び出したときにメソッドの名前から想定されるよりも多くの処理を行っている場合は、主説明に記載しておいたほうが良いでしょう[^3]。また、そのメソッドを呼び出したことによって後戻りができなくなるような場合も記載が必要です。

### 呼び出し元で意識しなければならない実装上の都合がある場合
メソッドの実装上の都合は、本来であれば呼び出し元には隠されるべきですが、どうしても呼び出し元で注意が必要な内容がある場合はその内容を記載しておく方が良いでしょう。


参考にさせていただいた[エンジニアのためのJavadoc再入門講座](https://www.shoeisha.co.jp/book/detail/9784798119489)は良書ですので、内容を詳しく知りたい方は読んでみてください。

気が向いたら4章のクラスのJavadocに記載すべき内容もまとめます。

[^1]: DDDでは定義域の管理はValue Objectの責務になるため、Value Objectのクラスドキュメントに記載するのでも良いと思います。
[^2]: 尤も、副作用があるメソッドは極力作らないようにしたいですね。
[^3]: このような場合は主説明を充実させるよりもメソッド名の変更を検討したほうが良さそうです。