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 ローカル変数

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 コレクション

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

4.25 実質的final(effectively final)

4.26 Stream API

4.27 Optional

4.28 ストリーム(InputStream OutputStream)

4.29 リソースの解放

4.30 例外

4.31 ガベージコレクション

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 オートボクシング

Javaにはintのようなプリミティブ型と、Integerのようなプリミティブ型の値をクラスとして扱うためのラッパークラスが存在し、
これらを意識せずコードを記述できるオートボクシングという機能があります。

しかし、オートボクシングを利用しているためにコーディング時に気づかない不要な処理を行っている可能性があるため、性能問題を避けるためには理解が必要です。

6.5.1 オートボクシング例

下記の2つの処理は等価ではありません。

「オートボクシングあり」の処理を等価なオートボクシングの無い処理に置き換えると以下の処理になります。

「オートボクシングあり」のコードをコンパイルするとバイトコード上は「オートボクシングなし」の処理を行うこととなり、
もし「キャストなし」の処理を意図していたなら、Integer#valueOfInteger#intValueの処理が不要な処理です。

6.5.2 オートボクシング性能

性能差が少ないため、ほとんど問題にはなりませんが、FindBugs等、静的解析で検出される問題のため、理解が必要です。

以下に計測結果を記載します。

6.6 StringからInteger・Longへの変換

数値文字列のStringIntegerに変換するには、Integer#valueOf(String)を利用して下記のように記述します。

String s = "1";
Integer value = Integer.valueOf(s);

しかし、下記のようにも記述できます。

String s = "1";
Integer value = new Integer(s);

これらの違いは、
new Integer(s)とした場合、必ずIntegerインスタンスが生成されますが、
Integer.valueOf(s)とした場合は -128から127の間の数値であればキャッシュから取り出すためインスタンスを生成しません。

このため、前者のInteger#valueOf(String)を利用した記述のほうが効率的です。
Long#valueOf(String)も同様です。

性能差が少ないため、ほとんど問題にはなりませんが、FindBugs等、静的解析で検出される問題のため、理解が必要です。

また、Stringからの変換だけでなく、intやlongからの変換も#valueOfが効率的ですが、オートボクシングを利用した場合、コンパイルで自動的にこれらの処理に変換されるため、記述することはありません。

6.7 Stringからint・longへの変換

数値文字列のStringintに変換するには、Integer#parseInt(String)を利用して下記のように記述します。

String s = "1";
int value = Integer.parseInt(s);

しかし、オートボクシングが利用できるため、意図せず下記のように記述ミスをする場合があります。

String s = "1";
int value = Integer.valueOf(s);//取得したIntegerインスタンスをオートボクシングでintにcastしている
String s = "1";
int value = new Integer(s);//生成したIntegerインスタンスをオートボクシングでintにcastしている

「オートボクシング」の説明に記載した通り、性能に差が出るだけでなく、
記述から明らかにミスであることが解るため、FindBugs等、静的解析で検出されるコードです。

longへの変換の場合はLong#parseLong(String)を利用します

以下に計測結果を記載します。

6.8 BigDecimalのZEROとの比較

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

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

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