プロジェクト

全般

プロフィール

Javaコーディング作法

Java SE 8環境におけるJavaプログラムのコーディング作法メモ。

はじめに

このページに書くコーディング作法は、プログラミング言語JavaとJava Standard Editionの範囲で開発するプログラムを対象としています。対象バージョンは、Java SE 8とします。

プログラムの入出力

コンソール(標準入力・出力)

コンソール出力をきれいに

プログラムの利用者にとって意味不明な文字列でコンソールを汚さないようにします。
デバッグや動作確認で必要なメッセージは、秩序を持って出力します。
System.out.printlnやprintfの安易な使用はコンソールを汚すので、ロギングAPIを使用するかそれに準じる出力を行います。ロギングAPIを使うときも、利用者がメッセージを峻別できるようルールを明確に定義します。例えば、開発者が使うデバッグ用のログは、レベルFINE, FINER, FINESTとする、等です。

ログ出力

ロギングAPIによって出力先の設定、出力書式の設定、出力の抑制を行いながら必要なメッセージをログに出力します。コンソール出力をきれいに保つには必要な機能です。

パッケージ構成

ロギングAPIでは、ロガー名に通常完全修飾名(パッケージ名付きのクラス名)を使います。その場合、パッケージ階層に応じて出力の抑制制御ができるようになります。例えば、次の階層構造でクラスが作られていたとします。

myapp
  +-- view
  |     +-- MainView
  |     +-- ProcessView
  +-- model
        +-- Hello

  • アプリケーション全体のログ出力レベルをCONFIG以上に指定する場合
    myapp.level = CONFIG
    
  • アプリケーション全体のログはWARNING、ただし表示系はFINE以上に指定する場合
    myapp.level = WARNING
    myapp.view.level = FINE
    

    などと指定できます。

GUI

マルチスレッドおよび並列処理

データ構造

メソッド

引数と戻り値とnull

引数とnull

nullを受け入れないメソッドのとき

API仕様としてnullを受け入れないときは、nullが渡されるのは事前条件違反(呼び出し側のバグでありコードの修正を要する)となるので、使用違反を実行時例外で通知します。

/**
 * 引数に指定したcandyを箱にしまう。
 * @param candy 箱にしまうcandy
 */
public void addCandy(Candy candy) {
    Objects.requireNonNull(candy);
    candyBox.add(candy);
  • @paramのコメントに、nullを受け入れ可能か否かをどのように記述するかを一貫して決めておく必要があります。
    • 例)nullを受け入れ可能な場合は@paramコメントに明示的に「nullを指定可能」と記述する。記述がない引数はnullを指定不可とする。
nullを受け入れたいメソッドのとき

引数でnullを受け入れる必要があるときは、メソッドのオーバーロードを使用して、引数のないメソッドと引数のあるメソッドを用意できないかを検討します。用意できる場合は、nullを引数に渡す代わりに、引数のないメソッドを呼びます。

public Optional<Candy> searchFirst() {
    // 最初に見つかったCandyを返却
}
public Optional<Candy> searchFirst(CandyColor color) {
    // 最初に見つかった、引数で指定した色のCandyを返却
}
  • colorにnullを入れて、色指定のない検索をするのではなく、色指定のない検索は引数なしのメソッドを呼ぶ

戻り値ではnullを返さない

戻り値がコレクションや配列のときは、空コレクションまたは空配列を返します。
戻り値の型に空オブジェクトが設計されているときは、それを返します。
それ以外のリファレンス型の場合は、Optional<T>を返すようにします。

最適化とメソッド

HotSpot VMのJITコンパイラは、繰り返し実行されるメソッドをネイティブにコンパイルします。また、サイズが小さなメソッドはインライン化の対象となります。JITコンパイラで最適化されやすいように、メソッドはシンプルに小さく作成するとよいでしょう。

エラー処理

エラー処理の種類

エラーは、プログラミングのバグと回復可能なエラーの2つに大別されます。

プログラミングのバグは、null参照、配列の領域外アクセス、ロジックの誤りなどでバグを修正しないと解決しないエラーです。
回復可能なエラーは、通常プログラムによるデータ検証の結果発生するもので、入力テキストの書式誤り、ネットワークの一時的な障害などがあります。プログラムはエラーからの回復が期待されるので、例えば利用者に通知する、再試行する、操作(処理)を破棄するなどの対応をします。

エラー処理機構

Javaでは、エラー処理をサポートする機能として次の3つが使われます。

  • 例外
  • アサーション
  • 戻り値

例外は、さらにチェックされる例外(checked exception)、実行時例外(runtime exception)およびエラー(error)の3種類があります。

エラー処理の種類とエラー処理機構の対応

例外 アサーション 戻り値
チェックされる例外 実行時例外 エラー
プログラミングのバグ
回復可能なエラー

プログラミングのバグには、実行時例外およびアサーションを使用します。
回復可能なエラーには、チェックされる例外および戻り値を使用します。
それぞれの使い分けについては次に示します。

例外を使う箇所

Effective Java 第3版 項目69より

  • 「例外的条件に対してのみ使うべきです。通常の制御フローに対しては、使うべきではありません。」
  • 「うまく設計されたAPIは、通常の制御フローに例外を使うことをクライアントに強制してはいけません。」

Effective Java 第3版 項目74より

  • 「常にチェックされる例外を個々に宣言し、Javadocの@throwsタグを使って各例外がスローされる条件を正確に文書化してください。」
  • 「メソッドがスローする可能性のあるチェックされない例外を・・・(中略)・・・、チェックされる例外と同様に剃られを注意深く文書化する」
  • 「チェックされない例外は、一般にプログラミングエラーを表しています」

例外の種類と使い分け

Effective Java 第3版 項目70より

  • 「呼び出しもとが適切に回復できるような状況に対してはチェックされる例外を使いなさい」
  • 「プログラミングエラーを示すには、実行時例外を使ってください。」
  • 「エラーはJVMが使うのに予約されているという慣習があります。」

Effective Java 第3版 項目71より

  • 「チェックされる例外を取り除く最も簡単な方法は、返したい結果の型のオプショナルを返すことです。」
  • 「例外をスローするメソッドを二つのメソッドに分割して、最初のメソッドは例外がスローされるかどうかを示すbooleanを返すようにすることで、チェックされる例外をチェックされない例外へ変えることもできます。」
  • 「もし、回復が可能であり、呼び出しもとに例外的状態の処理を強制したいなら、最初にオプショナルを返すことを検討してください。失敗の場合にオプショナルな情報を提供しない場合にだけ、チェックされる例外をスローすべきです。」

アサーション

Effective Java 第3版 項目49より

  • 「Java 7で追加された Objects.requireNonNull メソッドは柔軟かつ便利なので、null検査を手作業で行う必要はありません。」
  • 「Java 9では、範囲検査機構が java.util.Objecs へ追加されました。その機構は、 checkFromIndexSizecheckFromToIndexcheckIndex の三つのメソッドから構成されます。この機能はnull検査メソッドほどは柔軟ではありません。独自の例外詳細メッセージを指定できませんし、リストと配列のインデックスで使うためだけに設計されています。(両端を含む)閉区間は扱いません。しかし、それが行いたいことであれば、役立って便利です。」
  • 「publicではないメソッドは、次似示すように、アサーション(assertion)を用いてパラメータを検査できます。」

例外クラスの実装

Effective Java 第3版 項目70より

  • 例外クラスに、例外がスローされた状態に関する追加情報を提供するメソッドを定義する
    → 例外の文字列表現をクライアントに解析させないようにする

Effective Java 第3版 項目72より

  • 「Javaライブラリは、ほとんどのAPIが必要とする例外の大半を提供しています。」

Effective Java 第3版 項目73より

  • 「上位レイヤは下位レベルの例外をキャッチして、上位レイヤの中で、上位レベルの抽象概念の観点から説明可能な例外をスローすべきです。」

Effective Java 第3版 項目75より

  • 「エラーを記録する際に、例外の詳細メッセージ 注1 は、その原因となったすべてのパラメータとフィールドの値を含むべきです。」
    注1) その例外のtoStringメソッドの呼び出し結果である例外の文字列表現でクラス名に続く内容

禁じ手

これをやられると困る、ということを列挙します。

  • JavaVMの終了(System.exitコール)
  • GCの実行(System.gcコール)

null不安全

nullの可能性のある参照にtoStringメソッドの使用は避ける

引数で渡された変数などで、nullでないことが保証されない参照に toString を呼び出すとNullPointerExceptionを起こします。
null許容な参照の場合は、String.valueOf や java.util.Objects.toString などのnull時の振る舞いを持つメソッドを使います。
null不許容な参照の場合は、Objects.requireNonNull 等でnull検査をしてからtoStringを呼び出します(String.valueOfでも可)。

Javadocコメント

パッケージおよびクラスの仕様を、その利用者に説明するための文書の1つとして、ソースコードに所定の書式(Javadoc形式)で記載されたコメントからjavadocツールによって生成したものがAPI仕様書(APIリファレンス)になります。

したがって、クラス(型)の利用者から可視であるpublic、protectedな要素にJavadoc形式のコメントを記述していきます。
クラス利用者はソースコード上のコメントを直接参照するのではなく、ソースコード上のコメントからjavadocツールによって生成されたHTMLをWebブラウザ上で参照します。そのため、Javadoc形式のコメントはHTMLで見やすいことを念頭に記述します。

一方、パッケージスコープやprivateの要素は、クラス(型)の利用者ではなく、クラス(型)の開発者に向けたコメントを記述します。それは、API仕様書とは異なる視点で書かれ、またクラス(型)のソースコードと一緒に参照するので、ソースコード上で見やすいことを念頭に記述します。

本節は、API仕様書(APIリファレンス)を生成するためのドキュメントコメントの作法について記載します。

全般

パッケージの仕様を説明するJavadocコメントは、package-info.javaに記述します。
クラス(インタフェース、列挙型)の仕様を説明するJavadocコメントは、各ソースファイルのクラス定義の直前に記述します。

読み手に分かりやすいように、文章の他、箇条書き、使用例、表などを交えて説明するとよいです。

  • 文体は、「だ/である」調
  • 長いコメントの説明文章は最初の一文に要旨をまとめる
  • 変更履歴はコメントにしない(変更管理ツールおよび改訂毎のリリースノート等に書く)

概要説明文

コメントの最初の1文は、概要説明文として、クラス一覧、メソッド一覧、フィールド一覧の概要説明に抽出されます。最初の1文とは、Javadoc形式コメントを開始してから、最初のピリオド"."(日本語であれば句点)までです。

最初の1文には、その要素を端的に説明する一文を記述します。

説明文の段落分け

説明文を複数段落で記述するときは、2つ目以降の段落をそれぞれHTMLタグの段落要素(<p>…</p>)で囲みます。1つ目の段落には不要です。
改行にHTMLタグ<br>を使うのは極力避けます。箇条書きの方法は後述。

説明文章における見た目の修飾

  • 太字(強調文字)は、HTMLタグの<b>...</b>を使う
  • ソースコード上に出てくる用語(予約語、型名、パッケージ名、メソッド名、フィールド名など)は、{@code theName} を使う
    他のクラス・メソッドのJavadocドキュメントへのハイパーリンクを記載する{@link my.Class#member linkname}という記法もある。ただしjavadocツールで一緒に生成するクラス以外を指定した場合は、javadocのコマンドラインオプション-link-linkofflineで、参照したいJavadocのURLを指定する。
  • 大なり・小なり記号などHTMLタグに影響を及ぼす文字を使う場合は、{@literal inValue > outValue} を使う

箇条書き

箇条書きは、HTMLタグの箇条書き要素で記述します。HTMLの箇条書きは、番号なし(<ul>...</ul>)、番号付き(<ol>...</ol>)があります。

ソースコードブロック

複数行のソースコードを記述する場合は、HTMLタグのpreと{@code}を組み合わせて使用します。

<pre>{@code
public class Alfa extends Bravo {
    private Alfa() {}
}
}</pre>

個別

プロジェクト全体のドキュメント

開発・リリース単位において、overview.htmlを作って記述します。
ビルド方法、依存するライブラリ、対象Javaバージョン、ビルド時に生じるコンパイル警告、既知の問題点などを記述します。

また上位レベルの分割(サブシステム、パッケージ分割)などの設計思想を書きます。
パッケージ図、概念モデル図、主要シーケンス図などをUMLツールで作成し、その図を含めてもよいでしょう。

パッケージのドキュメント

パッケージ毎に1つ package-info.java ファイルを作成し、そこにJavadoc形式のコメントで概要説明を記述します。

型(クラス、インタフェース、列挙型)のドキュメント

T.B.D.

フィールドのドキュメント

T.B.D.

メソッドのドキュメント

T.B.D.

注意事項

コメントでは改行していてもJavadoc文書(HTML)では改行されない

改行が必要な単位を段落タグ<p>~</p>で示します。

/**
 * キューに詰まれているアイテム数を取得する。
 * <p>キューに詰まれ、まだ実行状態になっていない待機アイテムの数を計算して返却します。</p>
 *  :
 */
  • Java SE 7 までは<p>タグを単独で使用できましたが、Java SE 8からは<p>タグを単独で使用するとjavadocコマンドで警告となります。


1年以上前に更新