Javaコーディング規約

Future Enterprise Coding Standards

本コーディング規約は、世の中のシステム開発プロジェクトのために無償で提供致します。
ただし、掲載内容および利用に際して発生した問題、それに伴う損害については、フューチャー株式会社は一切の責務を負わないものとします。
また、掲載している情報は予告なく変更することがございますので、あらかじめご了承下さい。

1 はじめに

一般に利用・参照されている Java コーディング規約やガイドラインを以下に示す。本規約の作成においても、下記規約類を参照・抜粋している。

規約 著作者 URL
Code Conventions for the Java Programming Language Sun Microsystems http://www.oracle.com/technetwork/java/codeconvtoc-136057.html
Writing Robust Java Code Scott W. Ambler http://www.ambysoft.com/downloads/javaCodingStandards.pdf
オブジェクト倶楽部版 Java コーディング標準 オブジェクト倶楽部 http://objectclub.jp/community/codingstandard/CodingStd.pdf
電通国際情報際サービス版 Java コーディング規約 2004 電通国際情報サービス http://objectclub.jp/community/codingstandard/JavaCodingStandard2004.pdf
JJGuideline (Java - J2EE Conventions and Guidelines) Stephan.J & JCS Team http://www.fedict.belgium.be/sites/default/files/downloads/Java_J2EE_conventions_and_guidelines_EN.pdf
Google Java Style (非公式和訳) Google https://kazurof.github.io/GoogleJavaStyle-ja/
Acroquest Technology Java コーディング規約 Acroquest Technology https://www.acroquest.co.jp/webworkshop/javacordingrule/Acroquest_JavaCodingStandard_6_7.pdf
※現在は削除されています

※ Sun Microsystems の規約は Java 草創期から一応の標準という位置づけだったが、オブジェクト指向、及び、その開発環境の普及・発展によって、設計やコーディングにおいて、直接的に有用な知識や豊富な指針を含むような優れた規約や、ツールなどによる機械的な準拠チェックと連携する規約が普及してきている。

2 規約の重要性

標準としての規約を定義し、遵守することの重要性を以下に示す。

2.1 コーディングの心得

長いプログラムを記述すること(ステップ数)によって生産性が評価されたのは、過去の時代の出来事である。現在は、クラスやメソッドの役割が明確で、ロジックが読みやすく、保守性に優れたプログラムを記述することが評価される。コーディング規約は、コードの書き方に関する一種のパターンと考えることもでき、コードの保守性を向上させる具体的な方法を示している。したがって、規約の一つ一つの意図を理解し、守ることが重要になる。しかし、保守性に優れたコードを作成するためには、コーディング規約を守ることに加えて、良いコードを記述するための基本的な心構えをしっかり心に留めておく必要がある。以下では、その心得について述べる。

【コーディングの心得 5 か条】

  1. 見やすさを重視せよ
  2. ネーミングはわかりやすく
  3. サンプルを鵜呑みにしない
  4. 同じコードを二度書かない
  5. 役割は一つに

2.1.1 見やすさを重視せよ

「良いコード」の基本は、「他の人が読んでもわかりやすいと感じられるコード」。コードの見やすさは、フォーマットはもちろん、ロジックの簡潔さや API の常識的な使い方などから生まれる。コーディングにあたっては、常に他の人の視点を意識しながら、見やすさに気を配って記述する必要がある。例えば、自分で記述したコードであっても、しばらくたってから読み返してみると理解に時間がかかった経験は誰にもあるはず。「3 日前に書いたコードは他人のコードと同じ」ということもよく言われる。見やすさを重視することは、他の人のためだけでなく自分のためにもなる。コードを読んでもすぐに理解できないような実装は、再考(リファクタリング)の必要がある。

2.1.2 ネーミングはわかりやすく

コーディングでは、様々な変数やメソッドなどにネーミング(名前付け)する必要がある。ネーミングとは、本来、その対象の本質を表すような名前を考える作業である。大変難易度の高い作業だが、一方で適当に行ってもコードの動作は変わらないため、人によっては手を抜きがちとなる。しかし、ネーミングの良し悪しは、コードの可読性に非常に大きな影響を及ぼす。例えば、「C0001」というクラス名があるとする。これでは、何を表すクラスなのかすぐにはわからないだろう。また、「int p = 5000;」という記述があるとする。プログラマに聞くと、変数名 p は価格(Price)の略だと言うのだが、それならば略さずに、「int price = 5000;」としたほうが分かりやすいはずである。「ネーミングはわかりやすく」の背景には、読んで内容が理解できるという意味で、文章のようなプログラミングを行う、という考え方に基づく。

2.1.3 サンプルを鵜呑みにしない

サンプルコードを活用すること自体は、著作権等を侵害しなければ問題ない。問題なのは、その内容や背景を理解しないまま、サンプルコードだけを鵜呑みにして、「おまじない」として表面的に適用してしまうことである。コードを「おまじない」ととらえていては、サンプルコードの間違いを気づかないまま適用してしまうこともある。例えば、ストリームのクローズ処理を行っていないサンプルコードであっても、それに気づかずに自分のコードに適用してしまい、後で思わぬ障害を引き起こすという可能性がある。サンプルコードは、そこで説明する内容に絞ったコードが多いため、このような例はよく見られる。また、サンプルコードをそのまま適用した結果、自分が記述すべきコードには必要のないコードが含まれてしまう場合もある。その場合、コードの可読性を下げる原因となる。自分のコードは、自分で深く理解して記述すべきである。

2.1.4 同じコードは二度書かない

コードをコピー・ペーストしていませんか?コピー・ペーストしてしまうと、何らかの修正をする際に、全ての個所に同じ修正をする羽目になる。同じコードが現れるようならまとめて一つにし、外に出してコールするような書き方にすべきである。同じコードをまとめる作業は、どちらかといえば、コーディング時よりリファクタリング(ソフトウェアの外部的振る舞いを変更せずに内部構造を改善する作業)で行われることが多い。しかし、コーディング時からできるだけ気をつけておきたいことでもある。

2.1.5 役割は一つに

メソッドの役割が明確で、かつ 1 つであれば単体テストが行いやすくなる。つまり、コードの「試験性」が高まる。また、役割が一つであれば、後でコードを変更する際に修正箇所がわかりやすいため、障害修正に要する時間が短くなる。つまり、コードの「保守性」があがることになる。例えば、「チェックをして実行する」機能を実現するために、checkAndDo()メソッドが存在したとする。この場合、このメソッドは check()メソッドと do()メソッドに分割すべきである。なぜなら、checkAndDo()メソッドの check()ロジックに誤りがあった場合、do()メソッドに書かれる内容まで把握する必要が生じるためである。分割してあれば、check()メソッドだけの変更で済む。このことはクラスの設計にもあてはまる。

3 ネーミング規約

3.1 全般

3.2 パッケージ

3.3 クラス

3.4 メソッド

3.5 引数

3.6 変数全般

3.7 ローカル変数

3.8 Enum

4 コーディング規約

4.1 全般

4.2 フォーマット

4.3 コメント

4.4 インポート

4.5 コンストラクタ

4.6 メソッド

4.7 クラスメソッド

4.8 変数全般

4.9 定数

4.10 インスタンス変数

4.11 クラス変数

4.12 ローカル変数

4.13 引数

4.14 継承

4.15 インナークラス

4.16 メンバー順序

4.17 インスタンス

4.18 制御構造

4.19 文字列操作

4.20 数値

4.21 日付

4.22 三項演算子

4.23 switch 式

4.24 switch 文

4.25 コレクション

4.26 ラムダ式・メソッド参照・コンストラクタ参照

4.27 実質的 final(effectively final)

4.28 Stream API

4.29 Optional

4.30 var (Local-Variable Type Inference)

次のリンクも参考にしてください。
Style Guidelines for Local Variable Type Inference in Java

4.31 レコード

4.32 テキストブロック

次のリンクも参考にしてください。
Programmer’s Guide To Text Blocks > Style Guidelines For Text Blocks

4.33 ストリーム(InputStream OutputStream)

4.34 リソースの解放

4.35 例外

4.36 ガベージコレクション

5 コメント規約

5.1 よいコメントの鉄則

5.2 Java コメント(3 種類)の使い分け

Java では 3 種類のコメントが使える。javadoc コメントは/**で開始され、*/で終わる。C 風コメントは/*で開始され*/で終わる。単一行コメントは//で開始され、そのソースコード行が終わるまで続く。以下の表ではコメントの使い方とその例を示す。(コメントのスタイルに関しては、前述の「標準規約に準拠したコーディング例」を参照)

コメント種類 使用方法
javadoc コメント
/** comment */
interface、class、メソッド、フィールドの直前に書く。コメントは javadoc によって処理され、外部ドキュメント(HTML)として生成される。(この形式以外のコメントはドキュメントとして出力されないことに注意) /*
顧客(Customer)-
_ 顧客はわれわれがサービスまたは製品を売った人物
_ もしくは組織のいずれかである。
_ @author 開発太郎
_/
C 風コメント
/* comment */
特定のコードを無効化したいが、後で使用するかもしれないので残しておくためにコメント化する時や、デバッグ時に一時的に無効化するときに使用する。 /_
このコードは J.T.Kirk によって 1997.12.9 に前述のコードと置き換えたためコメント化した。2 年間不要であるならば削除せよ。
… (ソースコード)
_/
単一行コメント
// comment
メソッド内にて、ビジネスロジック、コードの概要、一時変数の定義内容などを記述する。 // 1995 年 2 月に開始された X 氏の寛大なキャンペーンで
// 定められた通り 1000$を超える請求には、全て 5%割引を
// 適用する。

※ ロジック中に、頻繁に C 風コメントでコメントを書くとまとめてコメントアウトする場合に不便なため、基本的にロジック中では単一行コメントを利用すること。

6 パフォーマンス

パフォーマンスを考慮した Java のコーディングについて以下に示す。

※ パフォーマンスは jre のバージョンやスペックによって変化します。本内容は jre1.8.0_74 での検証結果を元にした内容です。

※ 性能計測結果についての記載がありますが、あくまでも参考値です。性能を保証するものではありません。

6.1 Stream API

Java8 で追加された Stream API での記述は、可読性も高く、簡潔に書けますが、パフォーマンス・性能面で注意が必要な場合があります。

List の処理を行う際、拡張 for 文で処理する場合は Iterator インスタンスが 1 つだけ生成されますが、Stream API で処理する場合、最初の Stream インスタンスに加え、各中間処理ごとにも Stream インスタンスが生成され、その分の性能劣化が懸念されます。
以下に処理例と計測結果を記載します。

小中規模の処理量であれば考慮するほどの性能差はありませんが、大量の処理が見込まれる場合は考慮が必要です。
また、Stream API は並列処理(スレッド処理)の機能をサポートしていますので、利用できる場合は並列処理も含めての検証が必要です。

6.2 ラムダ式・メソッド参照・コンストラクタ参照

Java8 で追加されたラムダ式・メソッド参照・コンストラクタ参照は、匿名クラスを利用するよりも効率的です。
積極的な利用を推奨します。

以下に Comparator を生成した場合の計測結果を記載します。

ラムダ式は外部の変数を利用する場合、匿名クラスとほぼ同じ動作をします。

6.3 文字列連結

6.3.1 文字列連結(繰り返し)

文字列連結を繰り返し処理中で行う際、+演算子で処理することはアンチパターンとして知られています。
繰り返し処理中の文字列連結は、 StringBuilderStringJoinerStringBuffer を利用します。
また、コレクション要素の結合であればString#joinが利用できます。

以下に処理例と計測結果を記載します。

6.3.2 文字列連結(定数)

基本的に処理中の文字列連結では+演算子は使わないで処理するほうがパフォーマンスが高くなりますが、定数の場合は+演算子で定義するほうがパフォーマンスが高いです。

たとえば以下のように、処理したい場合、

private static final String CONST_A = "A";
private static final String CONST_B = "B";
private static final String CONST_AB = CONST_A + CONST_B;

StringBuilder で処理しようとすると以下のようになります。

private static final String CONST_AB = new StringBuilder(CONST_A).append(CONST_B).toString();

しかし、これらをバイトコード上で確認するとそれぞれ以下のようになります。

+演算子を利用した場合コンパイル時に最適化され、文字列"A""B"をあらかじめ結合して class が作成されます。
StringBuilderを利用した場合は最適化はされず、記述した通りの処理が行われます。

計測した場合、下記のようになります。

通常、定数処理を大量に処理することは考えられないので性能問題になることはありませんが、+演算子を利用したほうがパフォーマンスが高いこともあるということを理解してください。

6.4 List の種類

ListにはArrayListのようなRandomAccessを implements した、ランダムアクセスをサポートしているクラスと、
LinkedListのようなランダムアクセスをサポートしていない(シーケンシャルアクセス)クラスが存在します。
RandomAccessではないListは、List#getなどインデックスを利用するような操作のパフォーマンスが低いので注意してください。

以下に処理例と計測結果を記載します。

ランダムアクセスをサポートしているListがシーケンシャルアクセス(iterator を利用した処理など)で遅いということはないので、
ループの処理は拡張 for 文等、Iterator によるループで記述するのが無難です。
List#getでの処理をすべて禁止することはできませんが、高いパフォーマンスが求められる場合はListの種類にも注目してみてください。

6.5 BigDecimal の ZERO との比較

BigDecimal の正・負・ZERO の判定はBigDecimal#signumを利用します。
compareToを利用してBigDecimal.ZEROと比較しても同じことができますが、signumを利用したほうが効率的です。

以下に処理例と計測結果を記載します。

性能差が少ないので、必ずしも signum を利用する必要はありませんが、大量に処理する場合など、高いパフォーマンスが求められる場合は意識してください。


7 License

CC-By-4.0