Javaコーディング規約

Future Enterprise Coding Standards

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

# はじめに

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

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

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

# 規約の重要性

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

  • ソフトウェアメンテナンスにおける、可読性・保守性・拡張性の向上
  • 問題を起こしやすい実装を未然に回避することによる、品質・生産性の向上
  • 標準規約を通して得られる一般的な実装知識やノウハウ(=学習効果)

# コーディングの心得

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

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

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

# 見やすさを重視せよ

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

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

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

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

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

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

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

# 役割は一つに

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

# ネーミング規約

# 全般

  • 大文字・小文字の違いで名前を区別しない。

    良い例:

    private int carNumber;
    private int trainNumber;
    
    1
    2

    悪い例:

    private int num;
    private int Num;
    
    1
    2

# パッケージ

  • パッケージ名はすべて小文字にする
  • パッケージ名は意味のある名前にする
  • サブパッケージ名の重複は可能

# クラス

  • クラス名は単語の先頭を大文字にする
    良い例:

    public class Entry {
    
    1

    悪い例:

    public class entry {
    
    1
  • インターフェース名、Enum 名、Record 名はクラス名に準ずる

# メソッド

  • コンストラクタと同じ名前のメソッドはつくらない

  • メソッド名は区切りのみ大文字にする
    良い例:

    public String getName() {
        //・・・
    }
    
    1
    2
    3

    悪い例:

    public String getname() {
        //・・・
    }
    public String GETNAME() {
        //・・・
    }
    
    1
    2
    3
    4
    5
    6
  • 変換メソッド名は「"to"+オブジェクト名」にする
    良い例:

    public String toString() {
    
    1

    悪い例:

    public String string() {
    
    1
  • ゲッターメソッド名は「"get"+属性名」にする
    型がbooleanの場合は「"is"+属性名」にする

  • セッターメソッド名は「"set"+属性名」にする

  • boolean変数を返すメソッド名はtrue/falseの状態がわかるようにする

    良い例:

    public boolean isAsleep() {
    }
    public boolean exists() {
    }
    public boolean hasExpired() {
    }
    
    1
    2
    3
    4
    5
    6

# 引数

  • メソッドのパラメータ名とインスタンス変数名を一緒にしない
    ただし、アクセサメソッドやコンストラクタなど、統合開発環境の機能により自動生成するものに関しては可とする。
    アンダースコア _ をつけての区別は原則禁止とする。

    良い例:

    public double calc(double rate) {
        return this.value * rate;
    }
    
    1
    2
    3

    悪い例:

    public double calc(double value) {
        return this.value * value;
    }
    
    public double calc(double _value) {
        return this.value * _value;
    }
    
    1
    2
    3
    4
    5
    6
    7

# 変数全般

  • boolean変数はtrue/false の状態がわかるようにする

    良い例:

    private boolean isOpen;
    
    1

    悪い例:

    private boolean flag;
    
    1
  • 定数は全てstatic finalとし、すべて大文字、区切りは"_"

    良い例:

    private static final String SYSTEM_NAME = "販売管理システム";
    
    1
  • 変数名は小文字とし、単語の区切りのみ大文字にする

    良い例:

    private String thisIsString;
    
    1

    変数名に固有名詞が含まれる場合、先頭をのぞき、単語の区切り以外に大文字を使用してもよい

    良い例:

    private String thisIsIPAddress;
    
    1

# ローカル変数

  • スコープが狭い変数名は省略した名前でもよい
    良い例:

    if (・・・) {
        String s = "・・・・";
       //変数sを利用した処理 数行
    }
    
    1
    2
    3
    4

    悪い例:

    String s = "・・・・";
    if (・・・) {
       //変数sを利用した処理
    }
    ・・・
    if (・・・) {
       //変数sを利用した処理
    }
    
    1
    2
    3
    4
    5
    6
    7
    8

    変数sの利用範囲が広いので役割が明確になる変数名に変更する。

  • for 文のループカウンタは、ネストごとに"i","j","k"・・・を使う

# Enum

  • Enum 名はクラス名と同じく、単語の先頭を大文字にする

  • 列挙定数は定数と同じく、すべて大文字、区切りは"_"

    良い例:

    enum Season {
        WINTER,
        SPRING,
        SUMMER,
        FALL
    }
    
    1
    2
    3
    4
    5
    6

    悪い例:

    enum Season {
        winter,
        spring,
        summer,
        fall
    }
    
    1
    2
    3
    4
    5
    6

# コーディング規約

# 全般

  • 原則としてオブジェクトの参照にはインターフェースを利用する
    オブジェクトを参照する際は、そのオブジェクトの実装クラスを用いて宣言できるが、実装クラスに適切なインターフェースが存在している場合は、必ずインターフェースを用いて宣言すること。

    良い例:

    List<Entry> list = new ArrayList<>();
    Map<String, String> map = new HashMap<>();
    
    1
    2

    悪い例:

    ArrayList<Entry> list = new ArrayList<>();
    HashMap<String, String> map = new HashMap<>();
    
    1
    2
  • 推奨されない API を使用しない
    アノテーション@Deprecatedで指定されたメソッドは利用しないこと。

  • 使われないコードは書かない

  • 宣言は適切な権限で行うこと(public, protected, private

  • final を適切に利用する
    継承されないクラス、オーバーライドされないメソッド、値の変わらない変数(つまり定数)等、変化のないもの/変化させたくないものについてはfinal で宣言する。

    良い例:

    //継承されないクラス
    public final class CalculateUtils {
        //・・・
    }
    
    //値の変わらない変数(定数)
    private static final String MESSAGE = "・・・";
    
    //オーバーライドされないメソッド
    public final int sum(/*変化させたくない値*/final int... values) {
        int sumValue = 0;
        for (/*変化させたくない値*/final int value : values) {
            sumValue += value;
        }
        return sumValue;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

# フォーマット

  • インデントは空白文字 4 文字分の Tab を使用する

  • 長すぎる行は避ける

  • { の後にステートメントを記述しない
    良い例:

    if (s == null) {
        return 0;
    }
    
    1
    2
    3

    悪い例:

    if (s == null) {return 0;}
    
    1
  • 1 行に 2 つ以上のステートメントを記述しない
    悪い例:

    } catch (Exception e) {
        log.error("Error", e);return null;
    }
    
    1
    2
    3
  • カンマの後には空白文字を
    良い例:

    process(x, y, z);
    
    1

    悪い例:

    process(x,y,z);
    
    1
  • 代入演算子( = , += , -= , …)の前後には空白文字を挿入する
    良い例:

    int a = x;
    a += 10;
    
    1
    2

    悪い例:

    int a=x;
    a+= 10;
    
    1
    2
  • for 文内のセミコロンの後には空白文字を挿入する
    良い例:

    for (int i = 0; i < array.length; i++) {
        //・・・
    }
    
    1
    2
    3

    悪い例:

    for (int i = 0;i < array.length ;i++) {
        //・・・
    }
    
    1
    2
    3
  • ++-- とオペランドの間には空白文字を入れない
    良い例:

    i++;
    
    1

    悪い例:

    i ++;
    
    1
  • ビット演算子( |&^<<>> )の前後には空白文字を挿入する

  • 論理演算子( ||&&)の前後には空白文字を挿入する

  • 関係演算子( <>>=<===!= )の前後には空白文字を挿入する

  • 算術演算子( /% )の前後には空白文字を挿入する

  • return 文ではカッコを使わない
    良い例:

    int answer = (a + b + c) * d;
    return answer;
    
    1
    2

    悪い例:

    return ((a + b + c) * d);
    
    1
  • if などの条件式で boolean の変数を比較しない

    良い例:

    if (hasStock)
    
    1

    悪い例:

    if (hasStock == true)
    
    1
  • 不等号の向きは左向き( <<= )にする
    良い例:

    if (from <= x && x <= to) {
    
    1

    悪い例:

    if (x >= from && x <= to) {
    
    1

# コメント

  • ファイルの先頭への Copyright の表記について
    ソースのファイルヘッダにコピーライト標記は法的拘束力がないため、不要とする。
    ただし、顧客からの要求があった場合を除く。

  • Javadoc コメントには、少なくとも author と version(クラス)、param と return と exception(メソッド)を記述する

    • 今後もバージョンアップのリリースが予定されているソースでは、上記に加えて since(バージョン)を記述する
    • @Overrideのあるメソッドでは、上記に加えて{@Inherit}を記述する
  • Javadoc  クラスヘッダコメントのフォーマットは以下の通り

    良い例:

    /**
     * Action(or Bean)クラス メニュー名称
     *
     * @author 姓 名
     * @version バージョン YYYY/MM/DD 説明
     */
    
    1
    2
    3
    4
    5
    6
  • コメントは必要なものだけを簡潔に
    悪い例:

    /**
     * 文字列に変換
     */
    @Override
    public String toString() {
    
    /**
     * コピー
     *
     * @return コピーしたインスタンス
     */
    public Entry copy() {
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
  • 不要なコメントは記載しない

    • コードからすぐわかること・冗長なコメント
    • 名前の説明
      コメントではなくわかりやすい名前を付ける。
    • 別システムで管理している内容
      ソースコード管理システム、バグトラッキングシステムで管理している内容はソースコードにコメントで記載する必要はない。
      • コメントアウトされたコード
        ソースコード管理システムで管理されている

# インポート

  • java.langパッケージはインポートしない

    悪い例:

    import java.lang.String;//必要のない記述
    
    1
  • 原則として static インポートしない
    JUnit の作成やフレームワークとして static インポートが推奨されるような場合は利用してもよい

  • 原則としてオンデマンドのインポート宣言(type-import-on-demand declaration)(アスタリスク*によるインポート) は行わない

    悪い例:

    import java.util.*;
    
    1

# コンストラクタ

  • public 宣言していないクラスにはpublic権限のコンストラクタを作らない
    良い例:

    class Entry {
        //・・・
        Entry(int id) {
            //・・・
        }
    
    1
    2
    3
    4
    5

    悪い例:

    class Entry {
        //・・・
        public Entry(int id) {
            //・・・
        }
    
    1
    2
    3
    4
    5
  • インスタンスメンバを持たない(static メンバのみの)クラスは、private権限のコンストラクタを作成する

# メソッド

  • オーバーライドさせたくないメソッドはfinalを利用する

  • 戻り値が配列のメソッドで、戻る配列のサイズが 0 の場合、メソッドを使用するクライアントの余計な null チェックのロジックを回避するため、null ではなく長さゼロの配列を戻すようにする。 良い例:

    public String[] toArray(String s) {
        if (s == null || s.isEmpty()) {
            return ArrayUtils.EMPTY_STRING_ARRAY;
        }
        return new String[] { s };
    }
    
    public List<String> toList(String s) {
        if (s == null || s.isEmpty()) {
            return Collections.emptyList();
        }
        return List.of(s);
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    悪い例:

    public String[] toArray(String s) {
        if (s == null || s.isEmpty()) {
            return null;
        }
        return new String[] { s };
    }
    
    public List<String> toList(String s) {
        if (s == null || s.isEmpty()) {
            return null;
        }
        return List.of(s);
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
  • メソッドは 1 つの役割にする

# クラスメソッド

  • クラスメソッドを利用するときは、クラス名を使って呼び出す
    良い例:

    int comp = Integer.compare(x, y);
    
    1

    悪い例:

    Integer a = //
    int comp = a.compare(x, y);
    
    1
    2

# 変数全般

  • 1 つのステートメントには 1 つの変数宣言
    良い例:

    /** 科目コード */
    private String code;
    /** 科目名 */
    private String name;
    /** 科目略名 */
    private String shortName;
    
    1
    2
    3
    4
    5
    6

    悪い例:

    private String code, name, shortName;
    
    1
  • リテラルは使用しない
    リテラルとは、コード中に、表現が定数として直接現れており、記号やリストで表現することができないものを指す(数値、文字列両方含む 通称マジックナンバー)。コードの可読性・保守性の低下を防ぐために、リテラル定数(static final フィールド)を使用すること。
    例外:-1,0,1 等をカウント値としてループ処理等で使用するような場合

    良い例:

    private static final double ONE_MILE_METRE = 1609.344;
    
    public double mileToMetre(double mi) {
        return mi * ONE_MILE_METRE;
    }
    
    1
    2
    3
    4
    5

    悪い例:

    public double mileToMetre(double mi) {
        return mi * 1609.344;
    }
    
    1
    2
    3
    • リテラル定数の名前はその値の意味を正しく表現したものにする

      悪い例:

      private static final int ZERO = 0;
      
      1
  • 配列宣言は「型名[]」にする

    良い例:

    private int[] sampleArray = new int[10];
    
    1

    悪い例:

    private int sampleArray[] = new int[10];
    
    1
  • できるだけローカル変数を利用する
    ローカル変数で事足りるものをインスタンス変数として利用するなど、必要のないインスタンス変数を定義すると、パフォーマンスや可読性の低下やの大きな要因となる上、マルチスレッドを意識した際に不整合がおきる可能性があるので、インスタンス変数は必要性を充分に考慮してから使用すること。

  • 定数はfinalで宣言する

  • ローカル変数とインスタンス変数を使いわける

# 定数

  • public で宣言するクラス変数とインスタンス変数は、定数のみとし、 static final で定義する
    final ではない static な定数は作成しない。

    良い例:

    public static final String PROTOCOL_HTTP = "http";
    
    1
  • 定数( static フィールド)に、 static ではないメソッドから書き込まない

  • 定数は、プリミティブ型もしくは、不変(Immutable)オブジェクトで参照する

    • 不変Listの生成にはList.of()を利用する

      良い例:

      public static final List<Integer> VALUES = List.of(1, 2, 3, 4, 5);
      
      1

      悪い例:

      public static final List<Integer> VALUES = Arrays.asList(1, 2, 3, 4, 5);
      
      1
    • 不変Setの生成にはSet.of()を利用する

    • 不変Mapの生成にはMap.of()を利用する

      良い例:

      public static final Map<Integer, String> VALUES_MAP = Map.of(1, "A", 2, "B", 3, "C");
      
      1

      悪い例:

      public static final Map<Integer, String> VALUES_MAP = new HashMap<>() {
          {
              put(1, "A");
              put(2, "B");
              put(3, "C");
          }
      };
      
      1
      2
      3
      4
      5
      6
      7
    • 不変な配列インスタンスは長さ 0 の配列以外は生成不可能なため、外部から参照される(public)定数では利用せず、List等への置き換えをすること

      良い例:

      public static final List<Integer> VALUES = List.of(1, 2, 3, 4, 5);
      
      1

      悪い例:

      public static final int[] VALUES = { 1, 2, 3, 4, 5 };
      
      1

# インスタンス変数

  • インスタンス変数はprivateにする

    良い例:

    public class Employee {
        private long id;
    
        //・・・
        //getter/setter
    }
    
    1
    2
    3
    4
    5
    6

    悪い例:

    public class Employee {
        public long id;
    
        //・・・
        //getter/setter
    }
    
    1
    2
    3
    4
    5
    6

# クラス変数

  • public static final 宣言した配列を利用しない
    ※「定数」を参照

  • クラス変数にはクラス名を使用してアクセスすること

    良い例:

    BigDecimal b = BigDecimal.ZERO;
    
    1

    悪い例:

    BigDecimal a = //
    BigDecimal b = a.ZERO;
    
    1
    2

# ローカル変数

  • ローカル変数は利用する直前で宣言する
    行間の程度にもよるが、ある程度まとめて宣言するのは OK とする。

    良い例:

    for (int i = 0; i < lines.length; i++) {
        String line = lines[i];
        //lineの処理
    }
    
    1
    2
    3
    4

    悪い例:

    String line;
    for (int i = 0; i < lines.length; i++) {
        line = lines[i];
        //lineの処理
    }
    
    1
    2
    3
    4
    5
  • ローカル変数は安易に再利用しない
    一度宣言したローカル変数を、複数の目的で安易に使いまわさないこと。ローカル変数は、役割ごとに新しいものを宣言して初期化することにより、コードの可読性・保守性の向上、及びコンパイラの最適化の促進をはかる。

# 引数

  • メソッド引数への代入は行わない
    原則としてfinalで宣言する。

    良い例:

    public void add(final int value) {
        //・・・
    }
    
    1
    2
    3

# 継承

  • スーパークラスのインスタンス変数をサブクラスでオーバーライドしない
    スーパークラスと同じ名前のフィールドをサブクラスで宣言しないこと。 同じ名前のフィールドを宣言すると、スーパークラスのフィールドはサブクラスで宣言されたフィールドによって隠ぺいされてしまうので、他の人の混乱を招くことを防ぐため重複する名前は付けないこと。

    悪い例:

    public class Abs {
        protected String name;
    }
    
    public class Sub extends Abs {
        protected String name;//Abs#nameは隠ぺいされる
    }
    
    1
    2
    3
    4
    5
    6
    7
  • スーパークラスのメソッドをオーバーライドするときは@Override アノテーションを指定する。

    良い例:

    public class Abs {
        protected void process() {
    
        }
    }
    
    public class Sub extends Abs {
        @Override
        protected void process() {
    
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    悪い例:

    public class Abs {
        protected void process() {
    
        }
    }
    
    public class Sub extends Abs {
        //@Overrideアノテーションの指定がない
        protected void process() {
    
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
  • スーパークラスで private 宣言されているメソッドと同じ名前のメソッドをサブクラスで定義しない
    スーパークラスにある private メソッドと同じ名前のメソッドをサブクラスで定義しないこと。private メソッドはオーバーライドされず全く別のメソッドとして扱われ、他の人の混乱を招き、バグにつながる恐れがある。

# インナークラス

  • 原則としてインナークラスは利用しない
    一つの java ファイルに複数のクラスを記載するのは NG とする。また無名クラスを利用するのも原則として NG とする。
    Enum の定数固有メソッド実装(constant-specific method implementation)、Java8 のラムダ式は内部的にインナークラスとされるがこれらは許可する。

# メンバー順序

  • 以下の順で記述する

    1. static フィールド
    2. static イニシャライザー
    3. static メソッド
    4. フィールド
    5. イニシャライザー
    6. コンストラクター
    7. メソッド
  • 同一カテゴリー内では以下の可視性の順で記述する

    1. public
    2. protected
    3. パッケージ private
    4. private

# インスタンス

  • オブジェクト同士はequals()メソッドで比較する

    良い例:

    String s1 = "text";
    String s2 = "text";
    if (s1.equals(s2)) {
        //・・・
    }
    
    1
    2
    3
    4
    5

    悪い例:

    String s1 = "text";
    String s2 = "text";
    if (s1 == s2) {
        //・・・
    }
    
    1
    2
    3
    4
    5

    ただし Enum の場合は==演算子を利用して比較する

    equals()メソッドで比較する際、左辺のオブジェクトが null にならないように制御すること。

  • Class 名を利用した比較をおこなわない

    良い例:

    if (o instanceof Foo f) {
        // ...
    }
    
    1
    2
    3

    悪い例:

    if ("my.Foo".equals(o.getClass().getName())) {
        Foo f = (Foo)o;
        // ...
    }
    
    1
    2
    3
    4
  • インスタンスの型キャスト(Class キャスト)が必要な場合はパターンマッチングを使用する

    良い例:

    if (o instanceof String s) {
        // ...
    }
    
    var str = (o instanceof BigDecimal b) ? b.toPlainString() : String.valueOf(o);
    
    var empty = o == null ||
      (o instanceof String s && s.isEmpty()) ||
      (o instanceof Collection c && c.isEmpty());
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    悪い例:

    if (o instanceof String) {
        String s = (String)o;
        // ...
    }
    
    var str = (o instanceof BigDecimal) ? ((BigDecimal)o).toPlainString() : String.valueOf(o);
    
    var empty = o == null ||
      (o instanceof String && ((String)o).isEmpty()) ||
      (o instanceof Collection && ((Collection)o).isEmpty());
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

# 制御構造

  • 制御文( if , else , while , for , do while )の { } は省略しない

    良い例:

    if (s == null) {
        return;
    }
    
    1
    2
    3

    悪い例:

    if (s == null)
        return;
    
    1
    2
  • ステートメントが無い {} ブロックを利用しない
    悪い例:

    //{}内の記述が無い
    if (s == null) {
    }
    
    1
    2
    3
  • if / while の条件式で = は利用しない
    良い例:

    boolean a =//
    if (!a) {
        //・・・
    }
    
    1
    2
    3
    4

    悪い例:

    boolean a =//
    if (a = false) {//コーディングミス
        //・・・
    }
    
    
    boolean a =//
    boolean b =//
    if (a = b) {//おそらくコーディングミス
        //・・・
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  • forwhile の使い分けを意識する

  • for 文を利用した繰り返し処理中でループ変数の値を変更しない
    悪い例:

    String[] array = { /*・・・*/ };
    for (int i = 0; i < array.length; i++) {
        //・・・
        i += 2;//NG
    }
    
    for (String s : array) {
        //・・・
        s = "string";//NG
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  • for 文のカウンタは特別な事情がない限り、0 から始める

  • 配列やリストなどの全要素に対するループ処理は拡張 for 文を使用する。
    良い例:

    for (int value : array) {
        //・・・
    }
    
    for (String value : list) {
        //・・・
    }
    
    1
    2
    3
    4
    5
    6
    7
  • 配列をコピーするときはArrays.copyOf()メソッドを利用する

    良い例:

    int[] newArray = Arrays.copyOf(array, array.length);
    
    1

    悪い例:

    int[] newArray = new int[array.length];
    System.arraycopy(array, 0, newArray, 0, array.length);
    
    1
    2
  • 繰り返し処理中のオブジェクトの生成は最小限にする

  • if 文と else 文の繰り返しや switch 文の利用はなるべく避け、オブジェクト指向の手法を利用する
    良い例:

    CodingKind codingKind = toCodingKind(kind);
    d = codingKind.encode(s);
    
    //---
    
    CodingKind codingKind = toCodingKind(kind);
    s = codingKind.decode(d);
    
    1
    2
    3
    4
    5
    6
    7

    悪い例:

    switch (kind) {
    case 1 ->
        d = encode1(s);
    case 2 ->
        d = encode2(s);
    }
    
    //---
    
    switch (kind) {
    case 1 ->
        s = decode1(d);
    case 2 ->
        s = decode2(d);
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
  • 繰り返し処理の内部で try ブロックを利用しない
    特に理由がない場合は繰り返し処理の外にtryブロックを記載する。
    ただし、繰り返し処理内部で例外をキャッチし処理を行いたい場合は繰り返し処理の内部でtryブロックを利用してもよい。

    良い例:

    for (String s : array) {
        BigDecimal num;
        try {
            num = new BigDecimal(s);
        } catch (NumberFormatException e) {
            num = BigDecimal.ZERO;
        }
        //・・・
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

# 文字列操作

  • 文字列同士が同じ値かを比較するときは、equals()メソッドを利用する
    良い例:

    String s1 = "text";
    String s2 = "text";
    if (s1.equals(s2)) {
        //・・・
    }
    
    1
    2
    3
    4
    5

    悪い例:

    String s1 = "text";
    String s2 = "text";
    if (s1 == s2) {
        //・・・
    }
    
    1
    2
    3
    4
    5
  • 文字列リテラルはnew しない
    良い例:

    String s = "";
    
    1

    悪い例:

    String s = new String();
    
    1
  • 更新される文字列にはStringBuilder クラスを利用する
    良い例:

    StringBuilder builder = new StringBuilder();
    for (String s : array) {
        builder.append(s);
    }
    System.out.println(builder.toString());
    
    1
    2
    3
    4
    5

    悪い例:

    String string = "";
    for (String s : array) {
        string += s;
    }
    System.out.println(string);
    
    1
    2
    3
    4
    5

    スレッドセーフ性が保証されていない箇所では`StringBuffer`クラスを利用する

    ※パフォーマンスについても記載しているので参考にしてください

  • 1ステートメントのみで行われる文字列の連結には+演算子を利用する

    良い例:

    String s = s1 + s2;
    
    return s1 + s2 + s3 + s4 + s5;
    
    1
    2
    3

    悪い例:

    String s = new StringBuilder(s1).append(s2).toString();
    
    return new StringBuilder(s1).append(s2).append(s3).append(s4).append(s5).toString();
    
    1
    2
    3
  • 更新されない文字列にはString クラスを利用する

  • 文字列リテラルと定数を比較するときは、文字列リテラルのequals()メソッドを利用する
    良い例:

    private static final String PROTOCOL_HTTP = "http";
    
    if (PROTOCOL_HTTP.equals(url.getProtocol())) {
    
    }
    
    1
    2
    3
    4
    5

    悪い例:

    private static final String PROTOCOL_HTTP = "http";
    
    if (url.getProtocol().equals(PROTOCOL_HTTP)) {
    
    }
    
    1
    2
    3
    4
    5
  • プリミティブ型とString オブジェクトの変換には、変換用のメソッドを利用する
    良い例:

    int i = 1000;
    String s = String.valueOf(i);// "1000"
    s = NumberFormat.getNumberInstance().format(i);// 3桁区切り "1,000"
    
    boolean b = true;
    s = String.valueOf(b);// true/false
    s = BooleanUtils.toStringOnOff(b);// on/off
    
    1
    2
    3
    4
    5
    6
    7
  • 文字列の中に、ある文字が含まれているか調べるには、contains()メソッドを利用する

  • システム依存記号( \n\r など)は使用しない。
    悪い例:

    String text = Arrays.stream(array)
        .collect(Collectors.joining("\n"));
    
    1
    2

# 数値

  • 誤差の無い計算をするときは、BigDecimal クラスを使う
    浮動小数点演算は科学技術計算に利用するもので、誤差が発生する。これに対して、クラス「BigDecimal」は、文字列で数値の計算を行うので、金額などの正確な計算に適している。BigDecimal ではインスタンス生成時に指定された桁数での精度が保証される。

  • 数値の比較は精度に気をつける
    良い例:

    BigDecimal a = new BigDecimal("1");
    BigDecimal b = new BigDecimal("1.0");
    if (a.compareTo(b) == 0) {
        System.out.println("一致");
    }
    
    1
    2
    3
    4
    5

    悪い例:

    BigDecimal a = new BigDecimal("1");
    BigDecimal b = new BigDecimal("1.0");
    
    if (a.equals(b)) {
        System.out.println("精度が違うためこの分岐には入らない");
    }
    
    1
    2
    3
    4
    5
    6
  • 低精度なプリミティブ型にキャストしない

  • BigDecimalString変換する際はtoString()ではなくtoPlainString()を利用すること
    toString()を利用した場合、指数表記になることがあります。

# 日付

  • 日付の文字列のフォーマットには、SimpleDateFormatまたはDateTimeFormatterを使う
    良い例:

    Date date = new Date();
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
    String s = dateFormat.format(date);
    
    1
    2
    3

# 三項演算子

  • 入れ子の三項演算子の利用は禁止
    可読性が悪くなるので三項演算子を入れ子で行うのは禁止。

# switch 式

  • 一つの値を変数に代入するための if-else 文は代わりに switch 式の使用を推奨する
    switch 式の値を使用することで変数を不変(実質的 final)にでき、代入箇所が分散することによる可読性の低下を防げます。

    良い例:

    var value = switch (op) {
        case "add" -> a + b;
        default -> a - b;
    };
    
    1
    2
    3
    4

    悪い例:

    int value;
    if (op.equals("add")) {
        value = a + b;
    } else {
        value = a - b;
    }
    
    1
    2
    3
    4
    5
    6
  • case 句はなるべく一つの式での記述を推奨する
    複雑な式や複雑なステートメントを記述しなければならない場合は、メソッドに分割することを検討してください。

  • switch 式は、コーディングミスによるフォールスルーを避けるため、常にアロー構文を使用する
    https://docs.oracle.com/javase/jp/16/language/switch-expressions.html (opens new window)からの引用:

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

    良い例:

    var date = LocalDate.now();
    var off = switch (date.getDayOfWeek()) {
        case MONDAY -> {
            if (myCalendar.isOff(date) || localCalendar.isHoliday(date)) {
                yield true;
            }
            yield localCalendar.isHoliday(date.minusDays(1));
        }
        case TUESDAY, WEDNESDAY, THURSDAY, FRIDAY ->
            myCalendar.isOff(date) || localCalendar.isHoliday(date);
        case SUNDAY, SATURDAY -> true;
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    悪い例:

    var date = LocalDate.now();
    var off = switch (date.getDayOfWeek()) {
        case MONDAY:
            if (myCalendar.isOff(date) || localCalendar.isHoliday(date)) {
                yield true;
            }
            yield localCalendar.isHoliday(date.minusDays(1));
        case TUESDAY, WEDNESDAY, THURSDAY, FRIDAY:
            yield myCalendar.isOff(date) || localCalendar.isHoliday(date);
        case SUNDAY, SATURDAY:
            yield true;
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
  • アロー構文の、中カッコ、yieldを省略できる場合は必ず省略する
    良い例:

    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";
    };
    
    1
    2
    3
    4
    5
    6
    7
    8

    悪い例:

    var day = DayOfWeek.SUNDAY;
    var shortDay = switch (day) {
        case MONDAY -> {
            yield "M";
        }
        case WEDNESDAY -> {
            yield "W";
        }
        case FRIDAY -> {
            yield "F";
        }
        case TUESDAY, THURSDAY -> {
            yield "T";
        }
        case SUNDAY, SATURDAY -> {
            yield "S";
        }
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
  • Enum 値の switch 式で case 句が全ての Enum 値をカバーする場合は default 句はデッドコードとなるため記述しない
    良い例:

    var day = DayOfWeek.SUNDAY;
    var off = switch (day) {
        case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> false;
        case SUNDAY, SATURDAY -> true;
    };
    
    var day = DayOfWeek.SUNDAY;
    var off = switch (day) {
        case SUNDAY, SATURDAY -> true;
        default -> false;
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    悪い例:

    var day = DayOfWeek.SUNDAY;
    var off = switch (day) {
        case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> false;
        case SUNDAY, SATURDAY -> true;
        default -> false;
    };
    
    1
    2
    3
    4
    5
    6

# switch 文

  • 代わりに switch 式が使用できる箇所は switch 式を使用する

    • case 句でreturnを記述する場合は switch 文を使用して良い
  • case 句はなるべく 1 行のステートメントでの記述を推奨する
    複雑なステートメントを記述しなければならない場合は、メソッドに分割することを検討してください。

  • switch 文は、コーディングミスによるフォールスルーを避けるため、なるべくアロー構文を使用することを推奨する
    https://docs.oracle.com/javase/jp/16/language/switch-expressions.html (opens new window)からの引用:

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

    良い例:

    var date = LocalDate.now();
    switch (date.getDayOfWeek()) {
        case MONDAY -> {
            if (
                !myCalendar.isOff(date) && !localCalendar.isHoliday(date) &&
                !localCalendar.isHoliday(date.minusDays(1))
            ) {
                work();
            }
        }
        case TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> {
            if (!myCalendar.isOff(date) && !localCalendar.isHoliday(date)) {
                work();
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    悪い例:

    var date = LocalDate.now();
    switch (date.getDayOfWeek()) {
        case MONDAY:
            if (
                !myCalendar.isOff(date) && !localCalendar.isHoliday(date) &&
                !localCalendar.isHoliday(date.minusDays(1))
            ) {
                work();
            }
            break;
        case TUESDAY, WEDNESDAY, THURSDAY, FRIDAY:
            if (!myCalendar.isOff(date) && !localCalendar.isHoliday(date)) {
                work();
            }
            break;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
  • アロー構文を使用しない(コロンを使用する)場合、複数の値をマッチさせるときの case 句はカンマを使用して列挙する
    良い例:

    var day = DayOfWeek.SUNDAY;
    boolean off = false;
    switch (day) {
        case SUNDAY, SATURDAY:
          off = true;
          break;
    };
    
    1
    2
    3
    4
    5
    6
    7

    悪い例:

    var day = DayOfWeek.SUNDAY;
    boolean off = false;
    switch (day) {
        case SUNDAY:
        case SATURDAY:
          off = true;
          break;
    };
    
    1
    2
    3
    4
    5
    6
    7
    8

# コレクション

  • Java2 以降のコレクションクラスを利用する
    Vector クラス、Hashtable クラス、Enumeration 等は、特にこれらを利用する理由がなければ、インターフェースを統一する目的で、これらの代わりにList(ArrayList クラス)、Map(HashMap クラス)、Iterator を使用すること。List などのインターフェースを利用することで JDK1.2 で整理されたわかりやすいメソッドを利用でき、また、インターフェースの特性から呼び出し元を変更せずに実装クラスを変更することができる。

  • 特定の型のオブジェクトだけを受け入れるコレクションクラスを利用する

    良い例:

    List<Map<String, String>> list = new ArrayList<>();
    
    1
  • ListのソートはList.sort()を利用する
    Listクラスの要素をソートする際は Java8 で追加されたList.sort()を利用すること。
    Java 7 以前で利用されていたCollections.sort()は利用しないこと。

  • Collection.forEach()は利用しない。拡張 for 文の利用を検討する
    Java8 で追加されたメソッド。
    拡張 for 文を利用したほうが多くの場合でデバッグに有利であり、可読性においてもforEachの優位性は少ないため、forEachは原則利用しない。拡張 for 文を利用する。
    具体的には下記のメソッドを利用しないこと。

    • Collection#forEach
    • Set#forEach
    • List#forEach

    Map#forEachはこの限りではない

    良い例:

    for (String s : List.of("A", "B")) {
        //処理
    }
    
    1
    2
    3

    悪い例:

    List.of("A", "B").forEach(s -> {
        //処理
    });
    
    1
    2
    3

    ただし、メソッド参照で処理できる場合はforEachを利用する。
    (デバッグのデメリットがほとんどなく、他と比較して処理効率が良いため)

    良い例:

    List.of("A", "B").forEach(this::process);
    
    1

    悪い例:

    for (String s : List.of("A", "B")) {
        this.process(s);
    }
    
    1
    2
    3
  • Arrays.asList()は利用せず、List.of()を利用する
    Java9 で追加されたメソッド。
    配列をListに置き換える場合や、単純な固定のListを生成する際にはList.of()を利用する。

    • Arrays.asList()List.of()の違い
      List.of()で生成したListは、完全に不変(Immutable)なListで、
      Arrays.asList()で生成したListは、サイズのみ不変で、set等による値の操作が可能なListです。
      また、setを行った場合、Arrays.asList()に与えられた配列インスタンスにも影響します。

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

  • ラムダ式が利用できる箇所はラムダ式を利用してよい
    ※パフォーマンスについても記載しているので参考にしてください

  • ただし、メソッド参照・コンストラクタ参照が利用できる場合はメソッド参照・コンストラクタ参照を利用する

    良い例:

    String::compareToIgnoreCase
    
    1

    悪い例:

    (s1, s2) -> s1.compareToIgnoreCase(s2)
    
    1

    良い例:

    BigDecimal::add
    
    1

    悪い例:

    (b1, b2) -> b1.add(b2)
    
    1
  • ラムダ式記述の際、型宣言は省略記法で記述する

    良い例:

    (s1, s2) -> s1 + "=" + s2
    
    1

    悪い例:

    (String s1, String s2) -> s1 + "=" + s2
    
    1
  • 原則ラムダ式内の行数は 1 行とする
    複数行で利用したい場合は、privateメソッドを作成しメソッド参照を利用する

    良い例:

    this::getMax
    
    private int getMax(int i1, int i2) {
        if (i1 > i2) {
            return i1;
        } else {
            return i2;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    悪い例:

    (i1, i2) -> {
        if (i1 > i2) {
            return i1;
        } else {
            return i2;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
  • 原則ラムダ式は 1 行記述に限定するので、中カッコ、returnは必ず省略する

    良い例:

    (s1, s2) -> s1 + "=" + s2
    
    1

    悪い例:

    (s1, s2) -> {
        return s1 + "=" + s2;
    }
    
    1
    2
    3

# 実質的 final(effectively final)

  • 実質的 final を利用する
    変数宣言にfinalを記載しなくてよい。

# Stream API

  • 利用してよい
    ※パフォーマンスについても記載しているので参考にしてください

  • 並列ストリームは利用しないこと
    悪い例:

    Stream<?> s = list.parallelStream();
    Stream<?> s = list.stream().parallel();
    
    1
    2
  • StreamAPI 記述の際の改行位置は、各中間処理・末端処理前のピリオドの前で改行する

    良い例:

    List<Character> alphabetLower = list.stream()
        .filter(Character::isAlphabetic)
        .map(Character::toLowerCase)
        .toList();
    
    1
    2
    3
    4

    悪い例:

    List<Character> alphabetLower = list.stream().filter(Character::isAlphabetic)
        .map(Character::toLowerCase).toList();
    
    List<Character> alphabetLower = list
        .stream()
        .filter(Character::isAlphabetic)
        .map(Character::toLowerCase)
        .toList();
    
    1
    2
    3
    4
    5
    6
    7
    8
  • インデントは統合開発環境の提供するフォーマッタに合わせる

  • 中間処理の数は 3 つ(3 行)程度までを推奨する
    中間処理の記述が多くなると可読性も悪くなり、デバッグも難しくなるため、3 行程度を目安にロジックを検討すること。

  • コメントは、原則として処理中には記載しない
    難解になってしまった場合のみ処理中の記載を認める

    良い例:

    // クラスFooのフィールドStrの値で昇順にソートし、フィールドStrの要素を取得して処理する。
    fooList.stream()
        .sorted(Comparator.comparing(Foo::getStr))
        .map(Foo::getStr)
        .forEach(this::proc);
    
    1
    2
    3
    4
    5

    悪い例:

    fooList.stream()
        .sorted(Comparator.comparing(Foo::getStr)) //クラスFooのフィールドStrの値で昇順にソート
        .map (Foo::getStr) //フィールドStrの要素を取得
        .forEach(this::proc); //処理
    
    
    fooList.stream()
        //クラスFooのフィールドStrの値で昇順にソート
        .sorted(Comparator.comparing(Foo::getStr))
        //フィールドStrの要素を取得
        .map (Foo::getStr)
        //処理
        .forEach(this::proc);
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
  • Stream は極力変数代入しないこと
    Stream は中間処理、末端処理を行うと使用済みとなり、以降同じインスタンスは利用できない。
    変数代入はほとんどの場合意味をなさず、むしろミスの元となるため極力変数代入はしないこと。

    良い例:

    List<String> list1 = Stream.of("A", "B", "C")
            .map(String::toLowerCase)
            .toList();
    
    List<String> list2 = Stream.of("A", "B", "C")
            .map(s -> s + s)
            .toList();
    
    1
    2
    3
    4
    5
    6
    7

    悪い例:

    Stream<String> stream = Stream.of("A", "B", "C");
    Stream<String> stream1 = stream.map(String::toLowerCase);
    List<String> list1 = stream1.toList();
    
    Stream<String> stream2 = stream.map(s -> s + s);//コーディングミス streamは使用済のためエラーになる
    List<String> list2 = stream2.toList();
    
    1
    2
    3
    4
    5
    6

# Optional

  • Optional は同メソッド内で値を取り出す場合は極力変数代入しないこと
    Optional とその値の変数は同じものを示す名前となり、同じ意味の変数名が複数現れることで可読性が下がるため、Optional の変数代入は行わないこととする。

    良い例:

    Employee employee = findEmployee(employeeId)
            .orElseThrow(IllegalArgumentException::new);
    
    1
    2

    悪い例:

    Optional<Employee> employeeOpt = findEmployee(employeeId);
    Employee employee = employeeOpt.orElseThrow(IllegalArgumentException::new);
    
    1
    2

    直接、値を取り出すことなくOptionalでのみ扱う場合はOptionalを変数代入してもよい。

    良い例:

    Optional<Employee> employee = findEmployee(employeeId);
    
    Dept dept = employee.map(Employee::getDivision)
            .map(Division::getDept)
            .orElse(null);
    
    Role role = employee.map(Employee::getRole)
            .orElse(null);
    
    //-----
    
    Optional<Employee> employee = findEmployee(employeeId);
    //・・・処理
    return employee;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

# var (Local-Variable Type Inference)

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

  • 明確な方針で、利用する・利用しないを統一すること
    方針無く、varを混在させるとソースコードの見通しと保守性が悪くなります。
    各プロジェクトで、例えば以下ののような方針で統一してください。

    1. varを利用しない
    2. 原則varを利用する
    3. 右辺で、明確に型がわかる場合はvarを利用する

    以下で23について例を示します。

    • 原則varを利用する

      利用できる箇所は全てvarを利用します。

      良い例:

      var a = "";
      var b = 123;
      var c = new ArrayList<String>();
      
      1
      2
      3

      悪い例:

      var a = "";
      int b = 123;
      List<String> c = new ArrayList<>();
      
      1
      2
      3
      void methodA() {
          var a = "";
      }
      void methodB() {
          String a = "";
      }
      
      1
      2
      3
      4
      5
      6
    • 右辺で、明確に型がわかる場合はvarを利用する

      右辺をみて型がわかる場合は、全てvarを利用します。 それ以外はvarを利用してはいけません。

      良い例:

      var s = ""; // リテラルによって型が明確に判断できます
      var list1 = new ArrayList<String>(); // newによって型が明確に判断できます
      var list2 = (List<String>) map.get("p"); // キャストによって型が明確に判断できます
      var list3 = List.of("A", "B", "C"); // ファクトリーによって型が明確に判断できます
      
      1
      2
      3
      4

      プロジェクトで観点を決めるべき例:

      var b1 = s.isEmpty(); // `is`で始まるメソッドは通例としてbooleanを返します
      var b2 = Objects.equals(s1, s2); // `equals`メソッドは通例としてbooleanを返します
      var i1 = Objects.hash(s); // `hash`、`hashCode`メソッドは通例としてintを返します
      var i2 = Objects.compare(s1, s2); // `compare`、`compareTo`メソッドは通例としてintを返します
      
      1
      2
      3
      4

      悪い例:

      var a = e.getData(); // `e`の型と、メソッド定義がわからなければ型が判断できません
      
      1

# レコード

  • 明確な方針で、利用する・利用しないを統一すること
    方針無く、recordとクラスと JavaBeans 形式のクラスや Lombok の @Data の使用を混在させるとソースコードの見通しと保守性が悪くなります。
    各プロジェクトで、recordを利用しないか、recordの使用しても良い箇所について方針を決めた上で使用するようにしてください。 また、recordは JavaBeans とは互換性がないため使用している各種ライブラリの対応状況にも注意する必要があります。

    方針例: クラス内で処理する一時的なデータを表現するためだけにrecordを使用しても良い。

    // parentId と id をキーとして、重複を排除した uniqueItems を作成します。
    record Key(int parentId, int id) {
    }
    var map = new HashMap<Key, Item>();
    for (Item item : items) {
      var key = new Key(item.getParenId(), item.getId());
      map.putIfAbsent(key, item);
    }
    var uniqueItems = map.values();
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • 次の記述スタイルを標準とする
    ただし、フォーマッタを導入している場合はフォーマッタに合わせます。

    良い例:

    /**
     * 矩形を表すクラス
     *
     * @param x      矩形の左上隅の x 座標
     * @param y      矩形の左上隅の y 座標
     * @param width  矩形の幅
     * @param height 矩形の高さ
     */
    public record Rect(
        /* 矩形の左上隅の x 座標 */
        double x,
        /* 矩形の左上隅の y 座標 */
        double y,
        /* 矩形の幅 */
        double width,
        /* 矩形の高さ */
        double height) {
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    次にポイントを説明します。

    • {の後、}の前に改行する

    • レコードコンポーネント(パラメータ)のカンマの後に改行することを推奨する
      レコードコンポーネントが少なく、レコードコンポーネント名からでも意味が理解でき、改行がなくても可読性が低下しない場合は、改行を必要としません。
      改行を推奨する理由は以下です。

      • アノテーションを付与したときでも比較的読みやすい(アノテーション引数との混在による可読性の低下の回避)
      • レコードコンポーネントが多い場合も比較的読みやすい
    • レコードコンポーネントが多い場合、レコードコンポーネントへ直接コメントをつけることを検討する
      レコードコンポーネントの JavaDoc としては@param形式でレコード名の上部に記述しますが、このソースコードをテキストとしてみた場合、レコードコンポーネントの定義と@paramの説明とで距離が空いてしまう場合があり、型と説明を読むのに時間がかかってしまう可能性があります。
      また、使用する IDE によっては、アクセサから宣言へのジャンプを使用すると、レコードコンポーネント(パラメータ)の定義へジャンプするものがあります。レコードコンポーネントにコメントがあればすぐに説明を読むことができますが、JavaDoc しか記述しない場合は、ファイル上部へ移動して対応するレコードコンポーネントの説明を探さなければなりません。

      public record Rect(
          /* 矩形の左上隅の x 座標 */
          double x,
          /* 矩形の左上隅の y 座標 */
          double y,
          /* 矩形の幅 */
          double width,
          /* 矩形の高さ */
          double height) {
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
  • レコードのアクセサを上書きしない

    悪い例:

    public record Rect(
        double x,
        double y,
        double width,
        double height) {
    
        public double x() {
            return x;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

# テキストブロック

次のリンクも参考にしてください。
Programmer's Guide To Text Blocks > Style Guidelines For Text Blocks (opens new window)

  • 複数行の文字列を定義する際、文字列連結よりもテキストブロックを使用する

    良い例:

    String message = """
            複数行の文字列はテキストブロックを使用しましょう。
            文字列連結と違い、プラス記号や改行コードのエスケープシーケンスのような無駄を排除でき、
            より読みやすいソースコードで書くことができます。
            """;
    
    1
    2
    3
    4
    5

    悪い例:

    String message =
            "複数行の文字列はテキストブロックを使用しましょう。\n" +
            "文字列連結と違い、プラス記号や改行コードのエスケープシーケンスのような無駄を排除でき、\n" +
            "より読みやすいソースコードで書くことができます。\n";
    
    1
    2
    3
    4
  • 単一行の文字列を定義する際、テキストブロックは使用せず文字列リテラルを使用する
    ただし、二重引用符(")のエスケープを避ける目的ではテキストブロックを使用しても良い。

    良い例:

    String singleLine = "単一行の文字列です。";
    
    String message = """
            テキストブロックでは単一の二重引用符「"」にエスケープを使用する必要がありません。""";
    
    1
    2
    3
    4

    悪い例:

    String singleLine = """
            単一行の文字列です。""";
    
    1
    2
  • テキストブロック内では基本的に改行コードのエスケープシーケンス(\n)を使用しないが、読みやすさ向上の目的で改行コードのエスケープシーケンス(\n)を使用しても良い

    良い例:

    String multiLine = """
            複数行の、
            文字列です。
            """;
    
    String csv = """
            名前,説明,MIMEタイプ
            CSV,"Comma-Separated Valuesの略\nCharacter-Separated Valuesの意味で使用されることもある","text/csv"
            TSV,"Tab-Separated Valuesの略","text/tab-separated-values"
            """;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    悪い例:

    String multiLine = """
            複数行の、\n文字列です。
            """;
    
    1
    2
    3
  • テキストブロックで定義した文字列を処理する場合は、テキストブロックをローカル変数やフィールドへ代入してから使用することを推奨する

    良い例:

    String selectX = """
            SELECT
                ID,
                NAME
            FROM
                TABLE_X
            """;
    String selectY = """
            SELECT
                ID,
                NAME
            FROM
                TABLE_Y
            """;
    processValues(fetch(selectX, Entity1.class), fetch(selectY, Entity2.class));
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    悪い例:

    processValues(fetch("""
            SELECT
                ID,
                NAME
            FROM
                TABLE_X
            """, Entity1.class), fetch("""
            SELECT
                ID,
                NAME
            FROM
                TABLE_Y
            """, Entity2.class));
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    複雑な処理に直接テキストブロックを使用すると可読性を下げる可能性があります。

  • 3 つ以上続く二重引用符(")をエスケープする際は、最初の二重引用符にエスケープシーケンスを使用する

    良い例:

    String javaCode = """
            String message = \"""
                    テキストブロックです。
                    \""";
            System.out.println(message);
            """;
    
    1
    2
    3
    4
    5
    6

    悪い例:

    String javaCode = """
            String message = \"\"\"
                    テキストブロックです。
                    \"\"\";
            System.out.println(message);
            """;
    
    String javaCode = """
            String message = ""\"
                    テキストブロックです。
                    ""\";
            System.out.println(message);
            """;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
  • テキストブロックの開始引用符(""")は前の行の右端に記述する

    良い例:

    String message = """
            テキストブロックです。
            """;
    
    1
    2
    3

    悪い例:

    String message =
            """
            テキストブロックです。
            """;
    
    1
    2
    3
    4
  • テキストブロックのインデントは開始引用符(""")に合わせる必要はない

    良い例:

    String message = """
            テキストブロックです。
            """;
    
    1
    2
    3

    悪い例:

    String message = """
                     テキストブロックです。
                     """;
    
    1
    2
    3

    一見すると、読みやすく見えるかもしれませんが、変数名の変更によって簡単に崩れてしまい、修正するために多くの行の変更を強制することになるため、メンテナンス性が低下します。

  • テキストブロックで定義する文字列のインデントは基本的に周辺の Java コードに合わせてインデントする
    ただし、横に長い文字列などの可読性向上の目的で左端に揃えるのは良い。

    良い例:

    public class Foo {
        public void process() {
            String message = """
                    テキストブロックです。
                    """;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7

    悪い例:

    public class Foo {
        public void process() {
            String message = """
        テキストブロックです。
        """;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7

    良い例:

    public class Foo {
        public void process() {
            if (foo) {
                String message = """
    それはもう長い長いテキストブロックのためインデントするとエディタ上でテキストを見るためには横スクロールが必要になるかもしれません。
    """;
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    悪い例:

    public class Foo {
        public void process() {
            if (foo) {
                String message = """
                        それはもう長い長いテキストブロックのためインデントするとエディタ上でテキストを見るためには横スクロールが必要になるかもしれません。
                        """;
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • テキストブロックのインデントにスペース文字とタブ文字を混在させない

  • 文字列の最後に改行コードを入れずに、意図的にインデントした文字列を定義するとき終了引用符(""")の前の行の右端に\を使用する

    良い例:

    String text = """
                ABC
                DEF
                GHI\
            """;
    
    1
    2
    3
    4
    5

    悪い例:

    String text = """
            ABC
            DEF
            GHI""".indent(4);
    
    1
    2
    3
    4

# ストリーム(InputStream OutputStream)

  • ストリームを扱う API を利用するときは、try-with-resources 文で後処理をする

    良い例:

    try (InputStream inputStream = Files.newInputStream(Paths.get("foo.txt")) {
        //inputStreamに対する処理を記載
    }
    
    1
    2
    3
  • ObjectOutputStream ではreset()を利用する

# リソースの解放

  • リソース解放を必要とするクラスを利用するときは、try-with-resources 文で後処理をする

    良い例:

    try (InputStream inputStream = Files.newInputStream(Paths.get("foo.txt")) {
        //inputStreamに対する処理を記載
    }
    
    1
    2
    3
  • リソース解放を必要とするクラスを作成する場合はAutoCloseableimplementsする
    AutoCloseableimplementsすることで try-with-resources 文が利用できるようになります。

# 例外

  • catch 文で受け取る例外は、詳細な例外クラスで受け取る

    良い例:

    try (InputStream inputStream = Files.newInputStream(Paths.get("foo.txt")) {
        //・・・
    } catch (IOException e) {
        log.error("Error", e);
        throw e;
    }
    
    1
    2
    3
    4
    5
    6

    悪い例:

    try (InputStream inputStream = Files.newInputStream(Paths.get("foo.txt")) {
        //・・・
    } catch (Exception e) {//範囲が広すぎる例外クラスの利用はNG
        log.error("Error", e);
        throw e;
    }
    
    1
    2
    3
    4
    5
    6
  • Exception クラスのオブジェクトを生成してスローしない

  • catch ブロックでは基本、例外処理をする。ただし処理を書いてはいけない部分もあるので、その部分については、"// ignore" というコメントを記述すること。

  • 例外クラスは無駄に定義しない

# ガベージコレクション

  • 原則finalize()のオーバーライド実装は禁止
  • もしfinalize()をオーバーライドした場合はsuper.finalize() を呼び出す
  • アプリケーションからfinalize()を呼び出さない

# コメント規約

# よいコメントの鉄則

  • コードを明確化するコメントを書く
    コードにコメントを書く理由は、自分自身、一緒に仕事をしている人、後に関わる開発者にとってコードをより理解しやすいものにするためである。

  • コメント化する価値がないプログラムならば、実行するに値しない
    有用な格言。コメントは必須。

  • 過剰な装飾は使わない (例:見出し状のコメント)
    1960 年代から 1970 年代の典型的な COBOL プログラマにはアスタリスク( * )でコメントを囲った箱を書く習慣があった。彼らの芸術的な主張を表わしているのかもしれないが、率直に言えばそれは製品に加わるちょっとした価値に比べれば大きな時間の無駄である。かわいいコードではなくきれいなコードを書くはずである。さらに、コードを表示するディスプレイや印刷するプリントに使われるフォントはプロポーショナルだったりそうでなかったりして、箱をきれいに整列させることは難しい。

  • コメントはシンプルに
    かつて見たもっとも最良のコメントは、シンプルな要点をまとめた注釈であった。なにも本を書く必要はなく、他の人がコードを理解するに十分な情報を提供するだけでよいのである。

  • コードを書く前に先にコメントを記述する
    コードをコメント化する最良の方法は、コードを書く前にコメントを書くことである。それが、コードを書く前にコードがどのように動作するかについて考えるよい機会となり、コメントの存在を保障することにもつながる。少なくともコードを書いた時にコメントすべきである。コメントによってコードが理解しやすくなることで、コードの開発中にアドバンテージを得ることができる。コードにコメントを書く時間を費やせば、それによって得られるものがある。

  • コメントには、なぜそうなのかを書く。コードを読めば分かることを書かない
    基本的に、コードの一部分を見ればそれが何かを理解することはできる。例えば、以下のコードを見て、$1000 以上の注文については 5%ディスカウントされることは理解できる。なぜそうなのか?大きな注文ではディスカウントがつきものだというビジネスルールがあるのだろうか?大きな注文に時間限定サービスがあるのか、それともずっとサービスがあるのか?これを書いたプログラマの気前がよかったのか?
    どこかソースコード中か別な文書にコメントされていない限り、それがなぜなのかを知ることはできない。

    if (grandTotal >= 1000.00) {
        grandTotal = grandTotal * 0.95;
    }
    
    1
    2
    3

    なお、メソッドコメントには、適切な javadoc コメント(タグ)のほかに、以下の内容も可能な限り明記すること。

    • 副作用のある処理の場合は、その内容 (→ メソッドの引数オブジェクトがメソッド内で変更されるケースなど)
    • 既知のバグ (→ 判明しているが修正しないことにした場合など)
    • 影響のある事前条件、事後条件 (→ メソッドが正しく動作するための前提について)
    • 並行性 (→  マルチスレッドでアクセスされた場合の動作について)
    • 該当メソッドの使用例やサンプルコード
  • TODO コメント
    設計者確認待ち、共通処理の作成待ちなどの理由により、実装時に TODO がある場合、下記のようにコメントを記述する。
    (Eclipse の TODO コメント形式を採用)
    例)

    //TODO:ワークフローの仕様決定待ち 関連チケット#12345
    
    1

# 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 風コメントでコメントを書くとまとめてコメントアウトする場合に不便なため、基本的にロジック中では単一行コメントを利用すること。

# パフォーマンス

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

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

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

# Stream API

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

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

  • 拡張 for 文

    List<String> list = //数値文字列のList
    List<String> resultList = new ArrayList<>();
    for (String string : list) {
        if (string.endsWith("0")) {
            resultList.add(string);
        }
    }
    return resultList;
    
    1
    2
    3
    4
    5
    6
    7
    8
  • Stream API

    List<String> list = //数値文字列のList
    List<String> resultList = list.stream()
        .filter(s -> s.endsWith("0"))
        .toList();
    return resultList;
    
    1
    2
    3
    4
    5
  • 計測結果

    処理する List の件数 拡張 for 文 (ms) StreamAPI (ms)
    100 万件 7 9
    1,000 万件 88 114
    1 億件 949 1,026
    2 億件 1,822 2,081

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

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

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

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

  • 匿名クラス

    Comparator<String> c = new Comparator<String>() {
        @Override
        public int compare(String o1, String o2) {
            return o1.compareToIgnoreCase(o2);
        }
    };
    
    1
    2
    3
    4
    5
    6
  • ラムダ式

    Comparator<String> c = (o1, o2) -> o1.compareToIgnoreCase(o2);
    
    1
  • メソッド参照

    Comparator<String> c = String::compareToIgnoreCase;
    
    1
  • 計測結果

    処理件数 匿名クラス (ms) ラムダ式 (ms) メソッド参照 (ms)
    10 億回 380 0(計測不能) 0(計測不能)
    100 億回 6,374 0(計測不能) 0(計測不能)
    1 京回 (30 秒以上) 14 10

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

  • 匿名クラス

    new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return arg.equals("DESC") ? o2.compareToIgnoreCase(o1)
                                                : o1.compareToIgnoreCase(o2);
            }
        }
    
    1
    2
    3
    4
    5
    6
    7
  • ラムダ式

    Comparator<String> c = (o1, o2) -> arg.equals("DESC") ? o2.compareToIgnoreCase(o1)
                                        : o1.compareToIgnoreCase(o2);
    
    1
    2
  • 計測結果

    処理件数 匿名クラス (ms) ラムダ式 (ms)
    10 億回(パラメータあり) 571 572
    100 億回(パラメータあり) 9,900 9,864

# 文字列連結

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

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

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

  • +演算子

    String s = "";
    for (int i = 0; i < list.size(); i++) {
        String string = list.get(i);
        if (i > 0) {
            s += ",";
        }
        s += string;
    }
    return s;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • StringBuilder

    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < list.size(); i++) {
        String string = list.get(i);
        if (i > 0) {
            sb.append(",");
        }
        sb.append(string);
    }
    return sb.toString();
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • StringBuffer

    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < list.size(); i++) {
        String string = list.get(i);
        if (i > 0) {
            sb.append(",");
        }
        sb.append(string);
    }
    return sb.toString();
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • String#join

    return String.join(",", list);
    
    1
  • 計測結果

    処理する List の件数 +演算子 (ms) StringBuilder (ms) StringBuffer (ms) String#join (ms)
    1,000 件 5 0(計測不能) 0(計測不能) 0(計測不能)
    1 万件 1,016 1 1 1
    10 万件 (30 秒以上) 2 5 5
    100 万件 (30 秒以上) 29 42 51

# 文字列連結(定数)

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

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

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

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

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

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

  • +演算子

    private static final java.lang.String CONST_AB = "AB";
    
    1
  • StringBuilder

    private static final java.lang.String CONST_AB;
    
    static {};
       0  new java.lang.StringBuilder [20]
       3  dup
       4  ldc <String "A"> [8]
       6  invokespecial java.lang.StringBuilder(java.lang.String) [22]
       9  ldc <String "B"> [11]
      11  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [26]
      14  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [30]
      17  putstatic jp.co.packagename.ClassName.CONST_AB : java.lang.String [34]
      20  return
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

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

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

  • 計測結果

    処理回数 StringBuilder (ms) +演算子 (ms)
    5,000 万回 559 0(計測不能)
    1 億回 1,059 0(計測不能)

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

# List の種類

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

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

  • for 文(List#get(int)によるループ)

    int size = list.size();
    for (int i = 0; i < size; i++) {
        String s = list.get(i);
        //処理
    }
    
    1
    2
    3
    4
    5
  • 拡張 for 文

    for (String s : list) {
        //処理
    }
    
    1
    2
    3
  • forEach

    list.forEach(this::処理);
    
    1
  • 計測結果

    処理する List の件数 ArrayList
    for 文(List#get(int)によるループ) (ms)
    LinkedList
    for 文(List#get(int)によるループ) (ms)
    ArrayList
    拡張 for 文 (ms)
    LinkedList
    拡張 for 文 (ms)
    ArrayList
    forEach (ms)
    LinkedList
    forEach (ms)
    1 万件 0(計測不能) 73 0(計測不能) 0(計測不能) 0(計測不能) 0(計測不能)
    10 万件 0(計測不能) 7,576 0(計測不能) 0(計測不能) 1 2
    20 万件 0(計測不能) 17,740 0(計測不能) 0(計測不能) 0(計測不能) 0(計測不能)
    50 万件 0(計測不能) (30 秒以上) 0(計測不能) 2 0(計測不能) 2
    100 万件 1 (30 秒以上) 0(計測不能) 4 0(計測不能) 4
    1,000 万件 16 (30 秒以上) 8 45 6 44

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

# BigDecimal の ZERO との比較

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

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

  • compareTo 利用

    BigDecimal value = new BigDecimal("0.0");
    if (value.compareTo(BigDecimal.ZERO) == 0) {
    
    1
    2
  • signum 利用

    BigDecimal value = new BigDecimal("0.0");
    if (value.signum() == 0) {
    
    1
    2
  • 計測結果

    (単位:マイクロ秒)

    処理回数 compareTo 利用 (マイクロ秒) signum 利用 (マイクロ秒)
    1 京回 527
    max:26,367
    min:0
    424
    max:21,213
    min:0

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


# License

CC-By-4.0 (opens new window)