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

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

一貫性を保つ

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

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

Java 言語のルール

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

例外を無視しない

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

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

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

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

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

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

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

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)をキャッチすることで、エラーを適切に処理できます。ただし、この方法を採用する際は、その前に慎重に検討する必要があります。また、上記の観点から、このような処理を行っても安全である理由をコメントで説明してください。

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

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

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

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

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

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

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

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

Java ライブラリのルール

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

Java スタイルルール

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

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

/*

package com.android.internal.foo;

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

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

/**

public class Foo { ... }

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

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

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

あるいは

/**

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 設定と完全に合致させるには、次のようにインポートします。

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

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

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

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

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

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

推奨の方法

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

非推奨の方法

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

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

例:

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 文字です。

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

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

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

頭字語を単語として扱う

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

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

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

一時的なコードや、短期的な解決策、改善の余地のあるコードに対して 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 つのレベルがあります。

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)) }