SideCI Blog

自動コードレビューサービスSideCIを提供している株式会社アクトキャットのコーポレートブログです。



Javaのコーディング規約チェックツールCheckstyleの使い方、CIとの統合、オープンソースプロジェクトでの活用事例

目次

  • Checkstyle概要
  • CheckstyleとIDEの統合
  • Checkstyle標準のチェック項目のカテゴリー
  • Checkstyleとビルドツールの統合
  • Checkstyle標準のチェック項目のカテゴリー
  • 標準で配布されているコーディング規約
  • オープンソースプロジェクトとCheckstyle
  • ElasticSearchプロジェクトでのCheckstyleの利用
  • まとめ

Checkstyle概要

Checkstyleは、プログラマーがコーディング規約に則ったJavaコードを書くのを補助する開発ツールです。 以下は、Checkstyleのサイトからの、日本語訳した引用になります。

(Checkstyleは)コーディング規約のチェックという大切ではあるけれども退屈なタスクを自動化し、プログラマーの貴重な時間を節約します。 プロジェクトで定められたコーディング規約を確実に守りつつ、プロジェクトを進めるための非常に有用なツールです。

また、Checkstyleは、クラス設計やメソッド設計の問題を発見や、コードのレイアウト・フォーマットのチェックなどの、ソースコードの様々な側面を検査を行うことができます。 こうしたチェックの種類や詳細は、前述のChecksytleサイトのChecksをご覧ください。

Checkstyleのチェック項目は、豊富な設定パラメータでカスタマイズすることができるため、すでにコーディング規約がある場合でも、 概ねその規約に適合するルールを記述できます。 またプロジェクトのコーディング規約がまだない場合は、「Sun Javaコーディング規約」や、「Google Java Style」等の 広く利用されているコーディング規約のチェックを行う設定ファイルがあらかじめ用意されているので、 それらを使用することでコーディング規約をプロジェクトのプロセスに簡単に組み込むことができます。

Checkstyleでは、XMLファイルを使用してルールを記述します。 記述したルールに基づくチェックは、Checkstyle標準ではjavaコマンド及びantタスクで提供されていますが、 実プロジェクトで利用する場合は、IDEへの組み込み、及びビルドツールへの組み込みで使用することが一般的です。

IDEへの組み込みでは、各種IDE向けのプラグインが提供されているので、それらを利用することでプログラマは ソースコードを書きながらリアルタイムにチェックを行うことができます。

ビルドツールへの組み込みでは、現在広く利用されているビルドツールである、maven及びgradleが標準でCheckstyle用タスクを提供しており、 ビルドプロセスにCheckstyleのチェックを簡単に組み込むことができます。 ビルドツールへの組込を行うことで、プロジェクトのソースコード全体で規約が守れているかどうかを、常に確認することができます。

但し、ビルドツールへの組込は制約として非常に強力であり、1件でも指摘があればコードをマージ出来なくなります。 Checkstyleがまだ導入されていないプロジェクトや、コーディング規約が厳格な制約でない場合には、 その用途向けに作られたSideCIなどの専用のWebサービスを利用するのが良いかもしれません。

一つのXMLファイルによるルールの記述を利用して、IDEでのチェック、CIのプロセス(ビルドツールもしくはSideCI)でのチェックの両方を行うことができるので、 Checkstyleを利用するのであれば、両方でのチェックをプロジェクトに組み込むのがよいでしょう。

CheckstyleとIDEの統合

Checkstyleは、Eclipse, IDEA, Netbeans等、JavaのメジャーなIDEとプラグインで統合するだけでなく、 EmacsやVim、Atom、Sublime Textなどのより軽量なエディタ向けのプラグインも提供されているため、それらと統合して使用することができます。

IDEのプラグインを利用することで、リアルタイムでチェックを行い、違反をエディタ上に表示することができ、 また違反の一覧を表示しそこから違反箇所へジャンプする機能などが提供されます。 IDEへの統合を行うことで、プログラマーの日々のプログラミングの作業において、 ストレスなくコーディング規約に則ったコーディングを行うことができます。

Eclipse用のCheckstyleプラグインです。 リアルタイムでのチェック、違反の表示、一覧の表示と違反箇所へのジャンプなどの機能が提供されます。 また、このプラグインはCheckstyle設定ファイル(checkstyle.xml)のGUIベースの編集ツールとしても 非常に高機能で、Checkstyleのマニュアルをあまり見ずとも、ルールを記述していくことが可能です。

IntelliJ IDEA用のCheckstyleプラグインです。 Checkstyleでのチェックの実行、違反箇所へのジャンプなどの機能が提供されます。 設定ファイルの編集機能は提供されていないようです。

Netbeans用のCheckstyleプラグインです。 リアルタイムでのチェック、違反の表示、一覧の表示と違反箇所へのジャンプなどの機能が提供されます。 設定ファイルの編集機能は提供されていないようです。

Checkstyleとビルドツールの統合

Checkstyleのデフォルトでは、antとの統合がサポートされています。 また、現在に広く使われているビルドツールである、mavenやgradleの場合は、 ビルドツール側でCheckstyle用のプラグインが提供されています。 ビルドツールと統合することで、CI(継続的インテグレーション)にCheckstyleによるチェックを組み込み、 プロジェクト全体でコーディング規約が守られているかを常に確認し、保証することができます。

100%コーディング規約を守るのではなく、あくまでコーディングのガイドラインである、といった緩さの規約として運用する場合は、 ビルドツールとの統合は難しいかもしれません。その際には他の方法を検討すべきでしょう。

Checkstyle標準のチェック項目のカテゴリー

2017年12月時点の最新バージョンである、Checkstyleバージョン8.5では、 14のカテゴリーに分かれた154のルールが実装され、それぞれのルール毎に複数のパラメータで動作をカスタマイズできます。 ここですべてのルールを取り上げることは出来ないので、カテゴリー毎にどのような ルールが定義されているかの概要を記述します。

Annotations(アノテーションに関するルール)

アノテーションに関するルールを設定します。アノテーションの対象(クラス、メソッド、引数 etc.)毎に、 記述スタイル(アノテーションを実装と同一行に書くか別の行にするか、複数のアノテーションを同一行に まとめるか否か etc.)を定義します。また、@Override, @Deprecatedの使用の強制、@SuppressWarnings の使用制限などを記述できます。

Block Checks(Javaのブロックに関するルール)

Javaのブロック(中括弧で囲まれた領域)に関するルールを設定します。 中括弧前後の改行の配置や、空のコードブロックを許容するかなどを記述できます。

Class Design(クラス設計に関するルール)

クラス設計に関するルールを設定します。 主に、コンストラクタやメソッドの可視性(private〜public)に関するルールや、 その他の修飾子(final, abstract etc.)に関するルールを設定します。

Coding(コーディングスタイルに関するルール)

コーディングスタイル(コードの体裁)に関する多くのルール(43個)を設定します。 概ね、コーディングスタイルに関わるルールで、他のカテゴリーに入らないルールが、 このカテゴリーに集められているイメージです。 数が多いためすべてを記述することはできませんが、例えば、以下のようなルールが存在します。 - 配列の最後の要素の後ろにカンマをつけるかどうか - 空の文を許容するかどうか - if文、for文、try文などのネスト数の制限 - マジックナンバーの禁止 - switch文にdefault句を強制

Headers(ファイルヘッダーに関するルール)

各ソースファイルのヘッダー部分の記述に関するルールを設定します。 著作権やライセンスに関する記述など、プロジェクトで共通のヘッダーを利用する場合は、 このカテゴリーのルールを使用して、記述することができます。

Imports(import文に関するルール)

Javaのimport文に関するルールを設定します。 ワイルドカードによるimportの禁止、static importの禁止や、importの順番などを記述できます。 また、プロジェクトで使用を禁止するクラスがある場合、(例えばjava.sqlの使用を禁止等) importを禁止するルールを記述することで、実質的に使用されていないことをチェックできます。

Javadoc Comments(Javadocコメントに関するルール)

Javadocコメントに関するルールを設定します。 メソッドやフィールドの可視性毎にJavadocコメントの記述を強制したり、 またJavadocの@句(@param, @return etc.)の順序を強制することができます。

Metrics(メトリクスに関するルール)

主にメソッド・クラスの複雑度・クラスの依存関係数などのメトリクスを測定し、 一定以上の複雑度、依存関係をもつソースコード等を検出します。 リファクタリングが必要であろうソースコードを抽出することができます。

Miscellaneous(その他のルール)

他のカテゴリーに属さない、雑多なルールがこのカテゴリーに配置されています。 現在の実装では、以下のようなルールが配置されています。 - ソースコードやコメントのインデント方法 - 配列の記述方法(クラス名にを付けるか、変数名にをつけるか) - ファイル末尾に改行をつけるか - long値のリテラルで大文字Lの使用を強制(100L etc. 小文字のlは数字の1と見分けがつきにくいため) - TODOコメントの存在確認 - propertiesファイルのキーが重複していないことの確認 - Unicodeエスケープの使用禁止(UTF-8でそのまま記述することを推奨) - その他

Modifiers(修飾子に関するルール)

クラスやメソッド等の修飾子(private〜public, final etc.)に関するルールを設定します。 修飾子が複数ある場合の順番や、冗長な修飾子(interface型のmethodのpublic修飾子など)を 許容するかどうか、等が設定できます。

Naming Conventions(命名規則に関するルール)

各種命名規則に関するルールを設定します。 クラス名・メソッド名・引数名・ローカル変数名等、Javaで出現する名前の種類毎に、 正規表現でルールを記述出来るため、概ねどのような命名規則にも対応することができます。 またデフォルトの正規表現は、概ねJavaで一般的な命名規則に従ったものになっているため、 正規表現のパラメータを省略してルールを記述することで、一般的なJavaの命名規則に基づく チェックを行うことができます。

Regexp(正規表現でのルール)

正規表現を利用して、javaファイルやその他ファイル(propertiesファイルなど)の検査を行うためのルールを設定します。 任意の正規表現でルールを記述出来るため、標準のルールをつかって直接的に記述することが難しい、 プロジェクト固有の規約を記述する用途で、幅広く利用することができます。

Size Violations(サイズ違反に関するルール)

javaファイルの行数、メソッド毎の行数、行の長さ等、各要素のサイズに基づくルールを設定します。 過度にサイズが大きいファイル・メソッド等を検出することで、 リファクタリングが必要であろうソースコードを抽出することができます。

Whitespace(空白に関するルール)

空白に関するルールを設定します。 Javaの文法における各要素の前後に空白を入れるかどうかを、事細かに設定できます。 また、半角スペース以外のスペース文字がないことの確認するルール等も設定できます。

標準で配布されているコーディング規約

Javaプロジェクトで広く採用されているコーディング規約である、「Sun Javaコーディング規約」と 「Google Java Style」のルールを記述した設定ファイルが、Checkstyleのサイトで配布されています。 プロジェクトのコーディング規約をこれから決める場合は、これら広く採用されている規約を そのまま採用したり、必要な部分を抽出して採用したりすることで、規約を決めるコストを節減でき、 また、実績ある規約を採用することで、無用な問題を避けることができます。

  • Sun Java コーディング規約
    • Java Foundation Class(JDKに含まれる標準クラス群)のソースコードは、 Sun Javaコーディング規約に従って記述されているため、OpenJDK等のプロジェクトでは、 この規約が採用されています。
    • 1997年リリース版以降、すでに規約自体のメンテナンスが行われない旨アナウンスされてます。
    • 1997年以降の新しいJDKの拡張(アノテーション、ラムダ式等)に対応する規約がありません。
    • 1行80文字(テキスト端末の制限に由来)のルールなど、若干時代遅れと思われるルールが含まれています。
  • Google Java Style
    • 2013年に最初のリリースがされて以降、継続的にメンテナンスされ、現在の最新版は2017年9月リリース版です。
    • 比較的新しいオープンソースプロジェクトでは、よく採用されています。

Google Java Styleは、概ね一般的に受け入れやすい ルールとなっているため、これからプロジェクトのコーディング規約を決める場合であれば、 Google Java Styleをベースにするのがよいと思われます。

オープンソースプロジェクトとCheckstyle

オープンソースプロジェクトにおけるCheckstyleの利用状況

Githubで公開されているJavaプロジェクトから、Githubでの人気度を表すスター数のTop 10プロジェクトを抽出し、 Checkstyleがどのように利用されているか調査しました。調査結果は以下の通りです。

プロジェクト名 プロジェクト概要 checkstyle.xmlの有無 ビルドツールへの組込 コーディング規約
ReactiveX/RxJava 非同期プログラミング用API gradle 比較的少数のルールのみ採用
iluwatar/java-design-patterns デザインパターンのJavaによる実装 maven Google Java Styleベース
elastic/elasticsearch 分散検索エンジン gradle 比較的少数のルールのみ採用
square/retrofit 型安全なHTTPクライアントライブラリ maven Google Java Styleベース
square/okhttp Android向けHTTPクライアントライブラリ maven Google Java Styleベース
google/guava Google Core Libraries for Java - - Google Java Style
PhilJay/MPAndroidChart Android向けグラフライブラリ - -
JetBrains/kotlin プログラミング言語 - -
JakeWharton/butterknife Android向けView Injectionライブラリ gradle Google Java Styleベース
bumptech/glide Android向けメディア管理ライブラリ gradle 独自ルールで多数のチェックを実行

TOP 10 Javaプロジェクトの内、7プロジェクトでcheckstyle.xmlが提供され、それらのすべてのプロジェクトで、 mavenまたはgradleのビルドプロセスにCheckstyleが組み込まれています。 Github上で公開されているような、比較的新しいプロジェクトの場合、Checkstyleが広く利用されていることがわかります。

一方で、Tomcat(1999年〜), Struts(2000年〜), Spring(2003年〜)など、Checkstyle(2001年〜)と 同じくらい歴史が長いプロジェクトにおいては、あまり広くは利用されていないようです。 やはり、大規模プロジェクトの途中からCheckstyleを導入するのは、それなりにハードルが高いのかもしれません。 上記プロジェクトのレポジトリで、checkstyle.xmlが提供されているのはTomcatのみで、 そのTomcatの場合でも、ビルドツールへの組み込みは行われていない模様です。

ElasticSearchプロジェクトでのCheckstyleの利用

ここでは、githubのスター数Top10 Javaプロジェクトの一つである、ElasticSearchプロジェクトでの Checkstyleの利用方法を採り上げます。

ElasticSearchは、分散環境に対応した検索エンジンの実装で、現在Javaで最も活発に開発が行われている オープンソースプロジェクトの一つです。 ElasticSearchプロジェクトは、2010年に開発が開始された比較的新しいプロジェクトで、開発開始以降、 2017年12月現在で、29000回以上のコミット・900人以上による貢献が行われている、大規模プロジェクトです。

checkstyle.xmlの設定項目

Gitの履歴を見ると、ElasticSearchプロジェクトでのCheckstyle導入は、2016年初頭からのようです。 プロジェクトの途中からの導入であるため、チェックするルールは比較的少数のルールから初めています。 ルールを追加したり、やむを得ない事情ができた場合にはルールを外したりしながら、 すぐには直せない違反によるノイズを抑えつつ、順次チェックの拡充を図っているように見えます。 ReactiveX/RxJavaプロジェクトなどでも、同様の経緯を見て取ることができます。

たくさんのチェックを行っても、検出した問題を放置することになっては意味がありません。 大量のチェック違反を放置しておくと、それがノイズとなり、すぐに直さなくてはならない 違反を見逃すことになりかねません。 チェック違反を放置することになるのであれば、チェック自体を行わない方がよい、という考え方が 根底にあるものと思われます。

一方で、プロジェクトの開始からCheckstyleを利用している場合は、Google Java Styleを ほとんどそのまま採用しているプロジェクトが多いようです。 プロジェクトの最初からCheckstyleでのチェックを常に実行するのであれば、 実績あるコーディング規約をそのまま採用するのは合理的であると言えます。

プロジェクト固有のチェックの追加

ElasticSearchプロジェクトでは、分散処理での性能を確保するため、java.io.Serializableによる Javaデフォルトのシリアライズは使用せず、独自のシリアライズの仕組みを使用しています。 そのためプロジェクト全体で、java.io.Serializableインターフェースや、 serialVersionUIDフィールドの使用を禁止しています。 禁止が守られることをCheckstyleで確認するため、正規表現でのルールが使用されています。

<module name="RegexpSinglelineJava">
  <property name="format" value="serialVersionUID" />
  <property name="message" value="Do not declare serialVersionUID." />
  <property name="ignoreComments" value="true" />
</module>
<module name="RegexpSinglelineJava">
  <property name="format" value="java\.io\.Serializable" />
  <property name="message" value="References java.io.Serializable." />
  <property name="ignoreComments" value="true" />
</module>

上記の正規表現によるルールの記述は、単なる文字列によるマッチングであるため、Javaソースコードの 意味的には完璧なチェックにはなっていません。 例えば、本来意味的には許容すべき「serialVersionUIDFake」といったフィールド名も禁止してしまいますし、 逆にimport java.io.*でimportし、implements Serializableとすることで、 このルールを回避しSerializableを無理矢理使用することも可能です (その場合でも、import文での*の使用を禁止するルールにはひっかかります)。

しかし、そのような明らかに悪意があるようなケースをのぞけば、概ね実用上十分なチェックはできます。 Checkstyleのチェック用のクラスを独自に実装することで、意味的により厳密なチェックを行うことも可能ですが、 多くの場合、正規表現のルールを使用することで、必要十分なルールを記述することができます。

チェック例外の設定

プロジェクトでコーディング規約への準拠を徹底的に行うと決めたとしても、 例外的にチェック違反を許容したい場合は、どうしても生じます。

例えば、ElasticSearchプロジェクトのソースコードには、ANTLRツールにより自動生成された ソースが含まれますが、自動生成したソースにコーディング規約のチェックするのは意味がありません。 また、JNA(Java Native Access)などの、特殊なAPIを利用するソースコードでも、 例外的に違反を許容する必要が生じています。 また、ElasticSerchプロジェクトのソース内に、行の長さの制限を違反ソースがたくさんあることがわかっているものの、 すぐには修正を行わない、という決断をしたようで、それらのファイルもチェック違反の例外として設定されています。

このように例外を記述するために、ElasticSearchプロジェクトでは、Checkstyleの提供する仕組みである、 checkstyle_sppressions.xmlによる例外設定を使用しています。

<!-- These files are generated by ANTLR so its silly to hold them to our rules. -->
<suppress files="org[/\\]elasticsearch[/\\]painless[/\\]antlr[/\\]PainlessLexer\.java" checks="." />
<suppress files="org[/\\]elasticsearch[/\\]painless[/\\]antlr[/\\]PainlessParser(|BaseVisitor|Visitor)\.java" checks="." />

同様の例外は、ソースコードに直接@SuppressWarningsアノテーションを記述することでも実現できますが、 checkstyle_sppressions.xmlによる指定を利用すれば、ソースコード本体に余分記述を埋め込まずにすみます。

いずれにしろ、きちんと例外の設定方法を確立し、チェック違反を放置し大量のノイズを生み出すのを 避けることが重要です。大量のノイズは真に対応すべき問題を隠してしまうので、放置するぐらいであれば、 チェック自体を行わない方がよいでしょう。

まとめ

  • CheckstyleによるチェックをIDE、ビルドツールの両方に組み込むことで、コーディング規約に則った プログラミングをストレスなく行うことができるとともに、プロジェクト全体でコーディング規約が 守られていることを保証することができます。
  • プロジェクトの最初からCheckstyleを導入する場合は、Google Java Styleを採用すれば、 あらかじめ用意されたルールをそのまま使用できるので導入が容易になり、実績のある規約を 採用することで無用のトラブルを避けることができます。
  • プロジェクトの途中からCheckstyleを導入する場合は、最小限のルールから初めて、順次ルールを 増やしていくアプローチが有効です。チェック違反を長期間放置することになるのであれば、 真の問題を隠すノイズとなってしまうので、そのチェックは一旦外した方がよいでしょう。
  • プロジェクト固有の特殊な規約がある場合でも、正規表現によるルール記述などを工夫すれば、 必要十分なチェックを行えることが多いです。

SideCIを利用することで、プロジェクトのレビュープロセスに、Checkstyleでのチェックを簡単に組み込むことができます。 SideCIでは、「Sun Javaコーディング規約」や「Google Java Style」によるチェックをすぐに利用することができます。 また必要な場合には、プロジェクト独自のcheckstyle.xmlを記述し、それを元にチェックを行うことも可能です。

Checkstyleがまだ導入されていないプロジェクトの場合、はじめは多くの警告が出て戸惑うかもしれません。 SideCIではプルリクエスト単位で新しく書いたコードだけCheckstyleで確認していくことが出来るので、少しずつCheckstyleを導入していくことが出来ます。 また、指摘・ルールについて、無効化したいものなどについての議論を開始することも出来ます。 まだCheckstyleをプロジェクト全体に導入していない場合、無料でSideCIをお試し頂ければ幸いです。