コントリビューター向け AOSP Java コードスタイル

このページのコードスタイルは、Android オープンソース プロジェクト(AOSP)に Java コードを投稿する際の厳格なルールです。通常、このルールを遵守していない場合、Android プラットフォームへの投稿は認められません。既存のすべてのコードがこのルールに準拠しているわけではありませんが、新しいコードに関しては準拠が必要です。多様性を受容するエコシステムにするために使用する用語や避けるべき用語の例については、不適切な表現の回避をご覧ください。

一貫性を保つ

非常にシンプルなルールとして、「一貫性を保つ」ことが挙げられます。コードを編集する場合、その前にまず、時間をかけてコード全体を確認して、スタイルを判別してください。既存のコードで if 句の前後にスペースがある場合は、それに倣ってください。コードコメントがアスタリスクのボックスで囲まれている場合、コメントする際は、同じようにアスタリスクのボックスで囲んでください。

スタイル ガイドラインを設定することの要点は、コーディングに関する共通の言語を持つことにあります。共通言語によって、伝え方ではなく、伝えようとしている内容に集中してもらえるようになります。ここでは、共通言語を理解できるようにグローバルなスタイルルールを提示していますが、ローカルなスタイルも重要です。ファイルに追加したコードが既存のコードと大きく異なると、読み手のリズムを狂わせてしまいます。このようなことがないように注意してください。

Java 言語のルール

Android では、標準的な Java コーディング規約に加えて、以下のルールに沿ってコーディングする必要があります。

例外を無視しない

次のように、例外を無視するコードを作成してしまうことがよくあります。

  void setServerPort(String value) {
      try {
          serverPort = Integer.parseInt(value);
      } catch (NumberFormatException e) { }
  }

絶対にしないでください。「自分のコードは決してこのようなエラーの状態にならない」とか「このような問題に対処することはそれほど重要でない」と思われるかもしれません。しかし、この種の例外の無視は、コードの中に地雷を埋めるようなものです。誰かがいつかそれを踏むかもしれません。コード内のすべての例外を原則に基づいて処理する必要があります。具体的な処理方法は、ケースによって異なります。

「空の catch 句があれば、違和感を感じるはずです。確かにそれが正しい場合もありますが、少なくともまず確認する必要があります。Java では、違和感から目を逸らすことはできません。」 - James Gosling

許容される代替案は以下のとおりです(優先度順)。

  • メソッドの呼び出し元まで例外をスローします。
      void setServerPort(String value) throws NumberFormatException {
          serverPort = Integer.parseInt(value);
      }
    
  • 抽象化のレベルに適した新しい例外をスローします。
      void setServerPort(String value) throws ConfigurationException {
        try {
            serverPort = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            throw new ConfigurationException("Port " + value + " is not valid.");
        }
      }
    
  • エラーに適切に対処して、catch {} ブロックの値を適切な値に置き換えます。
      /** Set port. If value is not a valid number, 80 is substituted. */
    
      void setServerPort(String value) {
        try {
            serverPort = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            serverPort = 80;  // default port for server
        }
      }
    
  • 例外をキャッチして RuntimeException の新しいインスタンスをスローします。これは危険性の高い方法であり、このエラーが発生した場合はクラッシュさせるしかないことを承知したうえで行ってください。
      /** Set port. If value is not a valid number, die. */
    
      void setServerPort(String value) {
        try {
            serverPort = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            throw new RuntimeException("port " + value " is invalid, ", e);
        }
      }
    
  • 例外を無視することが適切であるという判断に確信を持てる場合は、最後の手段として例外を無視できます。ただし、納得できる理由をコメントする必要があります。
    /** If value is not a valid number, original port number is used. */
    
    void setServerPort(String value) {
        try {
            serverPort = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            // Method is documented to just ignore invalid user input.
            // serverPort will just be unchanged.
        }
    }
    

汎用的な例外をキャッチしない

例外のキャッチでは、手を抜いて次のようなコードを作成してしまうことがよくあります。

  try {
      someComplicatedIOFunction();        // may throw IOException
      someComplicatedParsingFunction();   // may throw ParsingException
      someComplicatedSecurityFunction();  // may throw SecurityException
      // phew, made it all the way
  } catch (Exception e) {                 // I'll just catch all exceptions
      handleError();                      // with one generic handler!
  }

絶対にしないでください。ほとんどの場合、汎用的な ExceptionThrowable のキャッチは適切ではありません(特に、Throwable には Error 例外が含まれるため、おすすめしません)。予期しない例外(ClassCastException などのランタイム例外を含む)がアプリレベルのエラー処理でキャッチされることになるため、危険です。このようなコーディングを行うと、コードのエラー処理のプロパティがわかりにくくなります。つまり、呼び出すコード内に誰かが新しいタイプの例外を追加した場合、その例外のエラーに関して別の方法で対処する必要があることをコンパイラが指摘しなくなります。ほとんどの場合、異なるタイプの例外を同一の方法で処理することはできません。

このルールのまれな例外として、テストコードやトップレベル コードが全種類のエラーをキャッチする場合があります(UI にエラーが表示されないようにするため、あるいは、バッチジョブを実行し続けるため)。このような場合、汎用的な Exception(または Throwable)をキャッチすることで、エラーを適切に処理できます。ただし、この方法を採用する際は、その前に慎重に検討する必要があります。また、上記の観点から、このような処理を行っても安全である理由をコメントで説明してください。

汎用的な例外のキャッチに代わる方法は次のとおりです。

  • 各例外をマルチキャッチ ブロックの一部として個別にキャッチします。たとえば、次のように指定します。
    try {
        ...
    } catch (ClassNotFoundException | NoSuchMethodException e) {
        ...
    }
  • コードをリファクタリングし、複数の try ブロックを使用して、きめ細かなエラー処理を実行できるようにします。IO を解析から分離し、それぞれのケースでエラーを個別に処理します。
  • 例外を再スローします。多くの場合、このレベルで例外をキャッチする必要はありません。メソッドがそのまま例外をスローするようにします。

例外を恐れることはありません。例外をキャッチしていないとコンパイラが表示しても問題ありません。大丈夫です。コンパイラは、コード内のランタイム問題を把握しやすくしているだけです。

ファイナライザを使用しない

ファイナライザは、オブジェクトのガベージ コレクションが行われるときに、コードのチャンクを実行するための方法です。ファイナライザはクリーンアップには便利ですが(特に外部リソースの場合)、ファイナライザがいつ呼び出されるかに関する保証はなく、まったく呼び出されない可能性もあります。

Android はファイナライザを使用しません。ほとんどの場合、代わりに適切な例外処理を使用できます。どうしてもファイナライザが必要な場合は、close() メソッド(または同等のメソッド)を定義して、いつそのメソッドを呼び出す必要があるのかを正確に記述します(例については、InputStream を参照)。その場合、ログのフラッディングが発生するおそれがなければ、ファイナライザから短いログメッセージを出力することをおすすめします(必須ではありません)。

インポートを完全修飾する

foo パッケージの Bar クラスを使用する場合、次の 2 つの方法でインポートできます。

  • import foo.*;

    import ステートメントの数が減る可能性があります。

  • import foo.Bar;

    使用するクラスが明確になり、管理者がコードを読みやすくなります。

すべての Android コードのインポートで、import foo.Bar; を使用してください。Java 標準ライブラリ(java.util.*java.io.*)と単体テストコード(junit.framework.*)に対して明示的な例外が作成されます。

Java ライブラリのルール

Android の Java ライブラリとツールの使用には、規則があります。場合によっては、その規則に重要な変更が加えられていることがあり、古いコードで使用されているパターンやライブラリが非推奨になっていることもあります。そのような古いコードを扱う際は、既存のスタイルをそのまま使用しても問題ありません。ただし、新しいコンポーネントを作成するときは、非推奨ライブラリは使用しないでください。

Java スタイルルール

Javadoc 標準コメントを使用する

すべてのファイルで、先頭に copyright ステートメントを記述し、次に package ステートメントと import ステートメントを記述し(各ブロックは空白行で区切ります)、最後にクラス宣言やインターフェース宣言を記述します。Javadoc コメント内で、クラスやインターフェースの機能について説明します。

/*
 * Copyright 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.foo;

import android.os.Blah;
import android.view.Yada;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * Does X and Y and provides an abstraction for Z.
 */

public class Foo {
    ...
}

クラスや非自明 public メソッドを記述する際は必ず Javadoc コメントを組み込み、記述するクラスやメソッドの具体的な内容について少なくとも 1 文で記述する必要があります。コメント文は、三人称の状態動詞で記述を開始します。

/** Returns the correctly rounded positive square root of a double value. */

static double sqrt(double a) {
    ...
}

あるいは

/**
 * Constructs a new String by converting the specified array of
 * bytes using the platform's default character encoding.
 */
public String(byte[] bytes) {
    ...
}

setFoo() のような自明の get メソッドや set メソッドの場合、Javadoc を記述しても「sets Foo」だけになるのであれば、Javadoc を記述する必要はありません。メソッドが高度な処理を行う場合(制約を適用する場合や重要な副作用がある場合など)は、そのメソッドに対してコメントを記述する必要があります。また、プロパティ「Foo」の意味が自明でない場合も、その意味をコメントで記述する必要があります。

public などのメソッドを記述する際に Javadoc を含めることにはメリットがあります。public メソッドは API の一部であるため、Javadoc が必要です。Android では、Javadoc コメントを記述するための具体的なスタイルは決められていませんが、Javadoc ツールのドキュメント コメントの記述方法の手順に沿ってコメントを記述するようにしてください。

メソッドを簡潔に記述する

可能な限り、メソッドは簡潔に、かつ焦点を絞ったものにします。長いメソッドが適切な場合もあるため、メソッドの長さに厳密な制限はありません。メソッドが 40 行程度以上になったら、プログラムの構造を損なわずに分割できないか検討してください。

標準的な場所でフィールドを定義する

フィールドは、ファイルの先頭か、そのフィールドを使用するメソッドの直前で定義します。

変数の範囲を制限する

ローカル変数の範囲を最小限に抑えます。これにより、コードの可読性と保守性が向上し、エラーが発生する可能性が低減されます。各変数は、その変数を使用するすべての行を囲む最も内側のブロックで宣言します。

ローカル変数は、最初に使用する時点で宣言します。ほとんどのローカル変数宣言にはイニシャライザが含まれています。変数を適切に初期化するための情報が十分でない場合は、宣言を延期してください。

try-catch ステートメントは例外です。チェック例外をスローするメソッドの戻り値で変数を初期化する場合は、try ブロック内で初期化する必要があります。try ブロックの外部で値を使用する必要がある場合は、try ブロックの前で宣言する必要がありますが、値はまだ適切に初期化できていません。

// Instantiate class cl, which represents some sort of Set

Set s = null;
try {
    s = (Set) cl.newInstance();
} catch(IllegalAccessException e) {
    throw new IllegalArgumentException(cl + " not accessible");
} catch(InstantiationException e) {
    throw new IllegalArgumentException(cl + " not instantiable");
}

// Exercise the set
s.addAll(Arrays.asList(args));

この問題は、try-catch ブロックをメソッド内にカプセル化することで回避できます。

Set createSet(Class cl) {
    // Instantiate class cl, which represents some sort of Set
    try {
        return (Set) cl.newInstance();
    } catch(IllegalAccessException e) {
        throw new IllegalArgumentException(cl + " not accessible");
    } catch(InstantiationException e) {
        throw new IllegalArgumentException(cl + " not instantiable");
    }
}

...

// Exercise the set
Set s = createSet(cl);
s.addAll(Arrays.asList(args));

ループ変数は、特に明白な理由がない限り、for ステートメント自体の内部で宣言します。

for (int i = 0; i < n; i++) {
    doSomething(i);
}

あるいは

for (Iterator i = c.iterator(); i.hasNext(); ) {
    doSomethingElse(i.next());
}

インポート ステートメントの順序を決定する

import ステートメントの順序は次のとおりです。

  1. Android のインポート
  2. サードパーティからのインポート(comjunitnetorg
  3. javajavax

IDE 設定と完全に合致させるには、次のようにインポートします。

  • 各グループ内ではアルファベット順に配置します。大文字は小文字の前に配置します(たとえば、Z は a より前)。
  • 各主要グループ(androidcomjunitnetorgjavajavax)間は空白行で区切ります。

もともと、順序に関するスタイル要件はありませんでした。そのため、IDE が常に順序を変更するか、IDE デベロッパーが自動インポート管理機能を無効にして手動でインポートを維持する必要がありました。この状況は好ましく思われていませんでした。Java のスタイルを調査すると、それぞれ好みのスタイルは大きく異なりました。そのため、Android 側で「順序を決めて一貫性を持たせる」ことが必要になりました。そこで、Google がスタイルを選択してスタイルガイドを更新し、IDE にそれを遵守させることにしました。これにより、IDE ユーザーがコードを作成すると、追加の作業を必要とせずに、すべてのパッケージ内のインポートがこのパターンに合致するようになりました。

このスタイルは次のように選択しました。

  • ユーザーが最初に確認する必要のあるインポートを上部に配置するようにする(android)。
  • ユーザーがあまり確認する必要のないインポートを下部に配置するようにする(java)。
  • ユーザーが簡単に遵守できるスタイルにする。
  • IDE が遵守できるスタイルにする。

静的インポートは、通常のインポートと同じ順序で配置された他のすべてのインポートの上に配置されます。

インデントにスペースを使用する

ブロックのインデントにはスペースを 4 つ使用します。タブは使用しません。判断に迷う場合は、周囲のコードに合わせます。

関数の呼び出しや割り当てなど、行の折り返しを行う場合は、インデントにスペースを 8 つ使用します。

推奨の方法

Instrument i =
        someLongExpression(that, wouldNotFit, on, one, line);

非推奨の方法

Instrument i =
    someLongExpression(that, wouldNotFit, on, one, line);

フィールドの命名規則を遵守する

  • 非 public、非 static フィールド名は m で始めます。
  • static フィールド名は s で始めます。
  • 他のフィールドは小文字で始めます。
  • static final フィールド(不変性が強い定数)には ALL_CAPS_WITH_UNDERSCORES を使用します。

例:

public class MyClass {
    public static final int SOME_CONSTANT = 42;
    public int publicField;
    private static MyClass sSingleton;
    int mPackagePrivate;
    private int mPrivate;
    protected int mProtected;
}

標準の中かっこスタイルを使用する

中かっこは、独立した行に配置せず、1 つ上のコード行のコードの後ろに配置します。

class MyClass {
    int func() {
        if (something) {
            // ...
        } else if (somethingElse) {
            // ...
        } else {
            // ...
        }
    }
}

条件ステートメントの前後には、中かっこが必要です。ただし、例外として、条件ステートメント全体(条件と本文)が 1 行に収まる場合は、すべてを 1 行で記述することもできます(必須ではありません)。たとえば、通常は次のように記述します。

if (condition) {
    body();
}

このような場合は、次のように記述することもできます。

if (condition) body();

以下の方法は禁止されています。

if (condition)
    body();  // bad!

行の長さを制限する

コード内の各行のテキストは 100 文字以下にします。このルールについてはさまざまな意見がありますが、現在のところ、1 行の最大文字数は、以下の例外を除いて 100 文字です。

  • コメント行の中にサンプル コマンドや 100 文字を超えるリテラル URL が含まれている場合は、カット&ペーストを容易にするため、その行は 100 文字以上にしても構いません。
  • インポート行は、ユーザーが内容を確認することがあまりないため、制限を超えても構いません(これにより、ツールの作成が容易になります)。

標準 Java アノテーションを使用する

アノテーションは、同じ言語要素を対象とする他の修飾子よりも前に付加する必要があります。シンプルなマーカー アノテーション(@Override など)は、言語要素と同じ行に記述できます。アノテーションが複数ある場合や、パラメータ化されたアノテーションがある場合は、1 行に 1 つずつアルファベット順に配置します。

Java の 3 つの事前定義済みアノテーションに関する Android の標準的な方法は次のとおりです。

  • 対象の要素の使用が推奨されない場合、@Deprecated アノテーションを使用します。@Deprecated アノテーションを使用する場合は、@deprecated Javadoc タグも使用して代替実装を指定する必要があります。なお、@Deprecated メソッドは引き続き機能します。@deprecated Javadoc タグのある古いコードを見つけた場合は、@Deprecated アノテーションを追加してください。
  • メソッドがスーパークラスの宣言や実装をオーバーライドする場合は、必ず @Override アノテーションを使用します。たとえば、@inheritdocs Javadoc タグを使用して、インターフェースではなくクラスから継承している場合は、そのメソッドが親クラスのメソッドをオーバーライドすることを示すアノテーションを追加する必要があります。
  • 警告を除去できない状況に限り、@SuppressWarnings アノテーションを使用します。不要な警告が「除去不可能」だと確認できた場合は、その警告に対して @SuppressWarnings アノテーションを使用し、他のすべての警告がコード内の実際の問題を反映するように設定する必要があります。

    @SuppressWarnings アノテーションが必要となる場合は、その前に TODO コメントを付けて「除去不可能」な状況について説明する必要があります。一般的に、このコメントにより、問題のあるインターフェースを持つ不適切なクラスであることが明示されます。たとえば、次のようになります。

    // TODO: The third-party class com.third.useful.Utility.rotate() needs generics
    @SuppressWarnings("generic-cast")
    List<String> blix = Utility.rotate(blax);
    

    @SuppressWarnings アノテーションが必要な場合、コードをリファクタリングして、アノテーションを適用するソフトウェア要素を分離する必要があります。

頭字語を単語として扱う

変数や、メソッド、クラスの名前を付ける際は、読みやすくするために、頭字語や略語を単語として扱います。

良い例 悪い例
XmlHttpRequest XMLHTTPRequest
getCustomerId getCustomerID
class Html class HTML
String url String URL
long id long ID

JDK と Android のコードベースは頭字語に関して一貫性がないため、コード全体で一貫性を保つのはほぼ不可能です。そのため、頭字語は常に単語として扱うようにしてください。

TODO コメントを使用する

一時的なコードや、短期的な解決策、改善の余地のあるコードに対して TODO コメントを使用します。このコメントは、すべて大文字の文字列 TODO で開始し、その後にコロンを付けます。

// TODO: Remove this code after the UrlTable2 has been checked in.

あるいは

// TODO: Change this to use a flag instead of a constant.

「今後の予定」を示す形式として TODO を使用する場合は、具体的な日付(2005 年 11 月までに修正)や具体的なイベント(プロダクション ミキサー全員がプロトコル V7 を理解したらコードを削除)を指定します。

ロギングの回数を減らす

ロギングは必要ですが、相応の回数で簡潔に行わないと、パフォーマンスに悪影響を与え、有用性を失います。ロギング機能には、次の 5 つのレベルがあります。

  • ERROR: 致命的な問題が発生した場合に使用します。つまり、ユーザーが認識できるほどの結果が生じ、一部のデータの削除や、アプリのアンインストール、データ パーティションのワイプ、デバイス全体の再フラッシュなど(あるいはそれ以上のこと)を行わなければ回復できないような問題が発生したケースが該当します。このレベルは常にログに記録されます。ERROR レベルでロギングするのが妥当な問題は、統計情報収集サーバーにレポートすべき問題だと見なすことができます。
  • WARNING: 予期しない重大な問題が発生した場合に使用します。つまり、ユーザーが認識できるほどの結果が生じるものの、待機やアプリの再起動、アプリの最新バージョンの再ダウンロード、デバイスの再起動など、明示的なアクションを行うことで、データを失うことなく復元できる可能性が高い問題が発生したケースが該当します。このレベルは常にログに記録されます。WARNING レベルでロギングするのが妥当な問題も、統計情報収集サーバーにレポートすべき問題だと見なすことができます。
  • INFORMATIVE: 注目すべき現象が発生したことを記録するために使用します。つまり、広範囲にわたって影響を及ぼす可能性を持つ状況が検出されたケースが該当します。これは必ずしもエラーではありません。このような状況をログに記録するのは、対象ドメイン内で最高の権限を持つと考えられるモジュールだけに限定するべきです(これにより、権限のないコンポーネントによる重複ロギングを避けることができます)。このレベルは常にログに記録されます。
  • DEBUG: 予期しない動作の調査やデバッグを行ううえで重要と思われるデバイス上の状況を記録するために使用します。コンポーネント上で発生している現象に関する十分な情報を収集するのに必要な分だけをロギングするようにしてください。デバッグログがログの大半を占めてしまう場合は、詳細ログを使用することをおすすめします。

    このレベルは、リリースビルドでもログに記録され、if (LOCAL_LOG) ブロックまたは if LOCAL_LOGD) ブロックで囲む必要があります。なお、LOCAL_LOG[D] はクラス内やサブコンポーネント内で定義するため、このようなロギングをすべて無効にすることも可能です。そのため、if (LOCAL_LOG) ブロック内にアクティブ ロジックを組み込まないようにしてください。また、ログ用のすべての文字列構築を、if (LOCAL_LOG) ブロック内に配置する必要があります。if (LOCAL_LOG) ブロックの外側で文字列構築が行われることになる場合、ロギング呼び出しをメソッド呼び出しにリファクタリングしないでください。

    依然として if (localLOGV) を使用しているコードも一部存在します。この名前は標準ではありませんが、許容されます。

  • VERBOSE: 上記以外のすべての状況で使用します。このレベルは、デバッグビルドの場合に限りロギングされ、if (LOCAL_LOGV) ブロック(または同等のもの)で囲む必要があります。これにより、デフォルトでコンパイルから除外されます。文字列構築は、リリースビルドからは削除され、if (LOCAL_LOGV) ブロック内に配置する必要があります。

  • VERBOSE レベルを除き、各モジュール内では、1 つのエラーのレポートを可能な限り 1 回だけに限るようにしてください。モジュール内の単一の関数呼び出しチェーン内では、最も内側の関数だけがエラーを返すようにし、同じモジュール内の呼び出し元は、問題の切り分けに特に役立つ場合に限り、ロギングを追加するようにしてください。
  • VERBOSE レベルを除き、各モジュール チェーン内で、下位モジュールが上位モジュールから無効なデータを検出した場合、下位モジュールは DEBUG ログにのみ、この状況をロギングするようにします。また、この下位モジュールによるロギングは、そのロギングを行わないと呼び出し元が入手できない情報がある場合に限るようにします。具体的には、例外がスローされる場合や(例外にはすべての関連情報が含まれます)、ロギングされる情報だけがエラーコード内に含まれる場合は、この状況をロギングする必要はありません。これは、フレームワークとアプリ間のインタラクションにおいて特に重要です。サードパーティ製アプリによってなんらかの現象が発生するとしても、フレームワークによって適切に処理できる場合は、DEBUG レベルよりも高レベルのロギングをトリガーしないようにしてください。INFORMATIVE レベル以上のロギングをトリガーすべき状況は、モジュールまたはアプリが同レベルまたは下位レベルからエラーを検出した場合に限られます。
  • ロギングするのが妥当な状況が何度も発生する場合は、同じ(または同様の)情報が重複してログをオーバーフローさせることのないように、レート制限メカニズムを実装することをおすすめします。
  • ネットワーク接続の損失は、一般的な現象で想定範囲内のものであり、むやみにロギングすべきではありません。アプリ内に影響をもたらすネットワーク接続の損失は、DEBUG レベルまたは VERBOSE レベルでロギングするようにしてください(どちらのレベルにするかは、リリースビルド内でもロギングするのが妥当なほどその影響が深刻かつ想定外であるかどうかで判断します)。
  • ファイル システム上に、サードパーティ製アプリがアクセスできる完全なファイル システムまたはサードパーティ製アプリの代わりとなる完全なファイル システムがあったとしても、INFORMATIVE よりも上位のレベルでロギングしないでください。
  • 信頼できないソースから無効なデータが送信されたとしても(共有ストレージ上のファイルや、ネットワーク接続経由で送信されるデータを含む)、想定範囲内のものと見なされます。そのようなデータが無効であることが検出された場合に、DEBUG よりも上位レベルのロギングをトリガーしないでください(ロギングする場合でも、可能な限り制限してください)。
  • String オブジェクトに対して + 演算子を使用すると、デフォルト バッファサイズ(16 文字)の StringBuilder インスタンスと、別の一時的 String オブジェクト(必要な場合)が暗黙的に作成されます。そのため、StringBuilder オブジェクトを明示的に作成しても、デフォルト + 演算子を使用した場合に比べて高コストになることはありません(はるかに効率的になる場合もあります)。Log.v() を呼び出すコードの場合、ログの読み取りが行われなくても、文字列の構築を含め、リリースビルド上でコンパイルと実行が行われます。
  • 他のユーザーも読み取り可能なロギングにし、リリースビルドで利用できるようにするには、難解さをなくし、簡潔で理解できるものにする必要があります。この要件は、DEBUG レベルまでのすべてのロギングに当てはまります。
  • 可能な限り、ロギングを 1 行にまとめてください。1 行には最大 80 文字または 100 文字まで使用できます。タグを含めて約 130 文字または 160 文字を超える長さは可能な限り避けてください。
  • 成功を報告するロギングは、VERBOSE よりも上位のレベルでは使用しないでください。
  • 再現が困難な問題の診断に一時的ロギングを使用する場合は、DEBUG レベルまたは VERBOSE レベルまでにとどめて、コンパイル時にロギングを無効にできるように if ブロックで囲んでください。
  • ログを通じたセキュリティ リークに注意してください。個人情報はロギングしないようにしてください。特に、保護されたコンテンツに関する情報はロギングしないでください。どの情報が個人情報や保護されたコンテンツになるのかを事前に把握することは困難であるため、フレームワーク コードを記述する場合は特に注意してください。
  • System.out.println()(ネイティブ コードの場合は printf())は使用しないでください。System.outSystem.err/dev/null にリダイレクトされるため、print ステートメントは認識できるほどの効果を持ちません。ただし、この呼び出しで発生する文字列構築はすべて引き続き実行されます。
  • ロギングの最も重要なルールは、特定のログが不必要に他のログをバッファから排除しないようにすることです。

Javatests スタイルルール

テストメソッドの命名規則に沿って記述し、テスト対象項目とテスト対象ケースの間の区切り文字としてアンダースコアを使用します。このスタイルにより、テスト対象となるケースを把握しやすくなります。たとえば、次のようになります。

testMethod_specificCase1 testMethod_specificCase2

void testIsDistinguishable_protanopia() {
    ColorMatcher colorMatcher = new ColorMatcher(PROTANOPIA)
    assertFalse(colorMatcher.isDistinguishable(Color.RED, Color.BLACK))
    assertTrue(colorMatcher.isDistinguishable(Color.X, Color.Y))
}