フューチャー技術ブログ

Java17対応版!Javaコーディング規約の紹介

フューチャーのGitHubリポジトリで公開しているJavaコーディング規約をJava17に対応させたのでその宣伝記事です。

Java17

先日の2021年9月14日にJava17がリリースされました。

Java17は長期サポートされる(LTS)バージョンです。前回のLTSのJava11からすると、3年ぶりのLTS版のリリースとなります。

Java12からJava17の新機能

前回のLTS版から追加されたみてJava17まででどんな機能が追加されたのかを簡単にまとめると以下です。(これら以外にもあります。)

  • switch式とswitch新構文
  • テキストブロック
  • レコード
  • パターンマッチング
  • シールクラス

次のページが大変参考になります。

Javaコーディング規約

フューチャーではFuture Enterprise Coding Standardsと名前をつけてコーディング規約を公開しています。その辺りの詳しい話は2016年に公開された記事を参照してください。

特にJavaコーディング規約は2016年に公開してからもJavaのアップデートに伴って更新し続けています。そのおかげかこのJavaコーディング規約は、現在、日本ではかなり有名になり(と勝手に思っています😅)、「独習Java新版」や「[増補改訂]良いコードを書く技術」などでもリンクが紹介されるほどです。

今回はこのJavaコーディング規約をJava17のリリースに伴って、Java17で追加された新機能や新構文についての規約の追加や変更を行いました。

以降で、Java17にどのような構文が追加されて、Javaコーディング規約でどのようなルールを追加したのか簡単に紹介します。

switch式

switch式は式として書けるswitch構文です。例えば次のように使用します。
(構文についての詳細は「Oracle Help Center | Java言語更新 > Switch式」などを参照してください。)

var day = DayOfWeek.SUNDAY;
var shortDay = switch (day) {
case MONDAY -> "M";
case WEDNESDAY -> "W";
case FRIDAY -> "F";
case TUESDAY, THURSDAY -> "T";
case SUNDAY, SATURDAY -> "S";
};

switch式が使用できない以前は、上記の例のような場合、変数宣言と代入を別の場所に記述する必要があるおかげで、変数を実質的finalにできず、Javaのラムダ式と一緒に使いにくいなどの問題がありましたが、switch式を使えばこの問題の多くを解決することができます。

関連するコーディング規約は次のリンクです。

switch式は便利なことが多いため、Javaコーディング規約でも積極的に使用するように推奨しています。

switch新構文

switch文は、breakの記述忘れによって起こるフォールスルーが原因で不具合を生みやすい構文です。そのため使用を避けることをコーディングルールとされることが少なくない構文でもあります。

Java17(正確にはJava14)では、switch式が追加されたついでに、case句の構文にアロー構文が使用できるようになりました。これを使用するとbreakを使用しなくてもそれぞれのcaseが独立しフォールスルーが起こらなくなります。
(構文についての詳細は「Oracle Help Center | Java言語更新 > Switch式」などを参照してください。)

var date = LocalDate.now();
switch (date.getDayOfWeek()) {
case MONDAY -> {
if (
!localCalendar.isHoliday(date) &&
!localCalendar.isHoliday(date.minusDays(1))
) {
work();
}
} // breakは不要です!
case TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> {
if (!localCalendar.isHoliday(date)) {
work();
}
}
}

関連するコーディング規約は以下のリンクです。

Javaコーディング規約では、フォールスルーの問題を避けるため、switch式とswitch文を記述する際はアロー構文の使用を推奨することにしました。

Oracle Help Center | Java言語更新 > Switch式」でもアロー構文を推奨する記載があります。次は「Oracle Help Center | Java言語更新 > Switch式」からの引用です。

ノート:case L ->ラベルの使用をお薦めします。case L:ラベルの使用時は、break文またはyield文の挿入を忘れがちです。これを忘れると、コード内で思いがけないフォール・スルーが発生する場合があります。
case L ->ラベルで、複数の文または式でないコード、あるいはthrow文を指定するには、それらをブロック内に囲みます。caseラベルが生成する値をyield文で指定します。

テキストブロック

テキストブロックは複数行のテキストをそのまま記述できる構文です。
(構文についての詳細は「Oracle Help Center | Java言語更新 > テキスト・ブロック」などを参照してください。)

String message = """
これはテキストブロックです。
複数行のテキストをそのまま複数行のテキストで書くことができます。
複数の文字列リテラルを+で連結して記述するよりも読みやすいです。
""";

テキストブロックについてのスタイルガイドが公式の「テキスト・ブロック・プログラマーズ・ガイド」の「Style Guidelines For Text Blocks(英語)」に記載されています。

Javaコーディング規約にはStyle Guidelines For Text Blocks(英語)を参考にして日本語で書いた内容が記載されています。

例えば、基本的にテキストブロックの中で\nを使うことを禁止しますが、記述しても良い場合について書いていたりします。

// CSVとしての1レコードをわかりやすくするために、1レコード内の改行コードデータについては`\n`を使用しています。
String csv = """
名前,説明,MIMEタイプ
CSV,"Comma-Separated Valuesの略\nCharacter-Separated Valuesの意味で使用されることもある","text/csv"
TSV,"Tab-Separated Valuesの略","text/tab-separated-values"
""";

レコード

レコードはJavaで名前付きタプルを実現しようと設計された新しい構文と機能です。

Oracle Technology Network Japan Blog | Javaにレコードが登場」が詳しいです。
構文としては次のように記述します。

record Rect(double x, double y, double width, double height) {
}

関連するコーディング規約は次のリンクです。

コーディング規約では、使用する箇所を明確にする必要があることや、JavaDocや記述スタイルについてのルールが追加されています。

パターンマッチング

Java17ではinstanceofの式に続けて変数名を書くことで、型がマッチした場合に変数がその型として使用できるようになる構文です。
(構文についての詳細は「Oracle Help Center | Java言語更新 > instanceofのパターン・マッチング」などを参照してください。)

if (o instanceof String s) {
var lower = s.toLowerCase();
// このように書く必要がない!
// var lower = ((String)o).toLowerCase();

// ...
}

(ちなみに、プレビュー機能を使用する場合、switchとパターンマッチングを組み合わせて使用することも可能です。)

関連するコーディング規約は次のリンクです。

とても便利なので、コーディング規約では、キャストの際に使用するように推奨しています。

最後に

その他、Java17の新機能や廃止機能に合わせていくつか(主にサンプルコードを)修正しています。是非一度見てみてください!

ちなみに、このJavaコーディング規約ですが、ルールが多くとても厳しいです。

これは、そのまま使うことをあまり想定していなくて、自分のチームで使用しないルールは、マークダウンから削除したり入れ替えたりして使うことを想定しているためです。各自が必要なルールを考えて追加するよりは不要なルールを削除する方が簡単なため、予め多くのルールを用意するようにしています。

「ルールが多すぎて、とてもじゃないけど守りきれないから結局使えない」と思う方は、いくつかを削除する前提で見ていただけると良いと思います。