AOSP Java コード スタイル (貢献者のための)

このページにあるコード スタイルは、Android Open Source Project (AOSP) に Java コードを貢献するための厳密なルールです。 これらのルールに従わない Android プラットフォームへの貢献は、一般に受け入れられません。 既存のコードすべてがこれらの規則に従っているわけではないことは認識していますが、すべての新しいコードが準拠することを期待しています。 より包括的なエコシステムのために使用する用語と避けるべき用語の例については、Coding with Respect を参照してください。

一貫性を保つ

最も単純なルールの 1 つは「一貫性を保つ」ことです。 コードを編集している場合、周囲のコードを見て、そのスタイルを判断するのに数分かかります。 そのコードが 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
    /** 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! }

これはやめましょう。 ほとんどの場合、一般的な ExceptionThrowableThrowableErrorClassCastException のような実行時例外を含む) がアプリ レベルのエラー処理に巻き込まれることを意味するので、危険なのです。 つまり、誰かがあなたが呼び出しているコードに新しいタイプの例外を追加しても、コンパイラはそのエラーを別の方法で処理する必要があることを指摘しません。 ほとんどの場合、異なるタイプの例外を同じように処理するべきではありません。

このルールのまれな例外は、テスト コードおよびトップレベルのコードで、すべての種類のエラーをキャッチする必要があります (UI に表示されるのを防ぐため、またはバッチ ジョブの実行を継続するため)。 このような場合、一般的な ExceptionThrowable) をキャッチして、エラーを適切に処理することができます。 しかし、これを行う前によく考えて、この文脈でなぜそれが安全であるかを説明するコメントを入れてください。

一般的な例外をキャッチする代替方法:

  • マルチキャッチブロックの一部として、たとえば、各例を個別にキャッチする。 IO とパースを分離し、それぞれのケースで個別にエラーを処理します。
  • 例外を再投与します。 多くの場合、このレベルで例外をキャッチする必要はなく、メソッドに例外をスローさせるだけです。

例外はあなたの友人であることを忘れないでください! 例外をキャッチしていないとコンパイラが文句を言っても、顔をしかめないでください。 笑ってください。 コンパイラは、あなたのコードの実行時問題をキャッチしやすくしてくれただけなのです。

ファイナライザーを使わない

ファイナライザーは、オブジェクトがゴミとして収集されるときに、コードのチャンクを実行させる方法です。 ファイナライザーは (特に外部リソースの) クリーンアップに便利ですが、いつファイナライザーが呼び出されるか (あるいは、まったく呼び出されないか) という保証はどこにもありません。

Android ではファイナライザーを使用しません。 ほとんどの場合、代わりに優れた例外処理を使用することができます。 If you absolutely need a finalizer, define a close() method (or the like) and document exactly when that method needs to be called (see InputStream for an example). In this case, it’s appropriate but not required to print a short log message from the finalizer, as long as it’s not expected to flood the logs.

Fully qualify imports

When you want to use class Bar from package foo, there are two possible ways to import it:

  • import foo.*;

    Potentially reduces the number of import statements.

  • import foo.Bar;

    Makes it obvious what classes are used and the code is more readable for maintainers.

Use import foo.Bar; for importing all Android code. An explicit exception is made for Java standard libraries (java.util.*java.io.*, etc.) and unit test code (junit.framework.*).

Java ライブラリのルール

Android の Java ライブラリおよびツールの使用には規約があります。 場合によっては、規約が重要な方法で変更され、古いコードでは非推奨のパターンやライブラリが使用されていることがあります。 そのようなコードで作業する場合、既存のスタイルを継続してもかまいません。 しかし、新しいコンポーネントを作成する場合は、非推奨のライブラリーを決して使用しないでください。

Java スタイルのルール

すべてのファイルは、一番上に著作権表示、次に package と import 記述 (各ブロックは空行で区切られます)、最後にクラスまたはインターフェイスの宣言があります。 Javadoc コメントでは、クラスまたはインターフェイスが何を行うかを記述します。

/* * Copyright 2020 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 { ...}

すべてのクラスと非自明なパブリック メソッドは、クラスまたはメソッドが何を行うかを説明する少なくとも 1 つの文を含む Javadoc コメントを記述する必要があります。 この文は、三人称の記述動詞で始まる必要があります。

/** 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) { ...}

あなたの Javadoc が “sets Foo” だけなら、setFoo() のようにつまらない get や set メソッドの Javadoc は書く必要がないのです。 もし、そのメソッドがもっと複雑なこと(制約を強制したり、重要な副作用があるなど)をするのであれば、それを文書化する必要があります。 プロパティ「Foo」が何を意味するのか明らかでない場合、それを文書化する必要があります。

あなたが書くすべてのメソッドは、パブリックかそうでないかにかかわらず、Javadoc の恩恵を受けるでしょう。 パブリック メソッドは API の一部であり、したがって、Javadoc が必要です。 Android は Javadoc コメントを書くための特定のスタイルを強制しませんが、Javadoc ツールの Doc コメントの書き方 の指示に従う必要があります。

短いメソッドを書く

実行可能な場合、メソッドは小さく、焦点を絞ったものにします。 私たちは長いメソッドが適切な場合もあることを認識しており、メソッドの長さに厳しい制限は設けていません。 メソッドが 40 行ほどを超える場合は、プログラムの構造に害を与えずに分割できるかどうかを考えてください。

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

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

変数スコープを制限する

ローカル変数のスコープを最小にする。 これにより、コードの可読性と保守性が高まり、エラーが発生する可能性が低くなります。 変数のすべての使用を囲む最も内側のブロックで、各変数を宣言してください。

ローカル変数は、最初に使用される時点で宣言する。 ほぼすべてのローカル変数の宣言にイニシャライザーを含める必要があります。 変数を賢明に初期化するための十分な情報がまだない場合、初期化できるまで宣言を延期する。

例外は try-catch ステートメントです。 チェックされた例外を投げるメソッドの戻り値で変数を初期化する場合、tryブロックの中で初期化しなければならない。 If the value must be used outside of the try block, then it must be declared before the try block, where it can’t yet be sensibly initialized:

// Instantiate class cl, which represents some sort of SetSet 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 sets.addAll(Arrays.asList(args));

However, you can even avoid this case by encapsulating the try-catch block in a method:

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 setSet s = createSet(cl);s.addAll(Arrays.asList(args));

Declare loop variables in the for statement itself unless there’s a compelling reason to do otherwise:

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

and

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

Order import statements

The ordering of import statements is:

  1. Android imports
  2. Imports from third parties (comjunitnetorg)
  3. java and javax

To exactly match the IDE settings, the imports should be:

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

元々、順序に関するスタイル要件はありませんでした。つまり、IDE は常に順序を変更するか、IDE 開発者が自動インポート管理機能を無効にして手動でインポートを保守しなければならなかったのです。 これは悪いことだと考えられていました。 Javaスタイルで質問したところ、望ましいスタイルは千差万別で、Androidは単純に “順序を選んで一貫性を持たせる “必要があると判断されました。 そこで、私たちはスタイルを選択し、スタイルガイドを更新し、IDEをそれに従わせるようにしました。 IDEユーザーがコード上で作業する際、余分なエンジニアリングの努力なしに、すべてのパッケージのインポートがこのパターンに一致するようになることを期待しています。

We chose this style such that:

  • The imports that people want to look at first tend to be at the top (android).
  • The imports that people want to look at least tend to be at the bottom (java).
  • Humans can easily follow the style.
  • IDEs can follow the style.

Put static imports above all the other imports ordered the same way as regular imports.

Use spaces for indentation

We use four (4) space indents for blocks and never tabs. When in doubt, be consistent with the surrounding code.

We use eight (8) space indents for line wraps, including function calls and assignments.

Recommended

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

Not recommended

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

Follow field naming conventions

  • Non-public, non-static field names start with m.
  • Static field names start with s.
  • Other fields start with a lower case letter.
  • Public static final fields (constants) are ALL_CAPS_WITH_UNDERSCORES.

For example:

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

Use standard brace style

Put braces on the same line as the code before them, not on their own line:

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

We require braces around the statements for a conditional. Exception: If the entire conditional (the condition and the body) fit on one line, you may (but are not obligated to) put it all on one line. For example, this is acceptable:

if (condition) { body();}

and this is acceptable:

if (condition) body();

but this is not acceptable:

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

Limit line length

Each line of text in your code should be at most 100 characters long. While much discussion has surrounded this rule, the decision remains that 100 characters is the maximum with the following exceptions:

  • コメント行に 100 文字より長いサンプル コマンドまたはリテラル URL が含まれている場合、カット アンド ペーストを容易にするために、その行は 100 文字より長くてもかまいません。
  • Import 行は人間が見ることはほとんどないので、制限を超えることができます (これはツールの記述も簡素化します)。

標準の Java 注釈を使用する

注釈は、同じ言語要素に対する他の修飾子の前に置かれるべきです。 単純なマーカー アノテーション (たとえば @Override) は、言語要素と同じ行にリストすることができます。 複数のアノテーション、またはパラメータ化されたアノテーションがある場合は、アルファベット順に1行に1つずつ記載してください。

Javaの3つの定義済みアノテーションのAndroid標準のプラクティスは次のとおりです。

  • @Deprecated@Deprecated@deprecated Javadoc タグも必要で、代替実装に名前を付ける必要があります。 さらに、@Deprecated@deprecated Javadoc タグがある場合は、@Deprecated 注釈を追加してください。
  • メソッドがスーパークラスからの宣言または実装をオーバーライドするときはいつでも @Override@inheritdocs Javadoc タグを使用し、(インターフェイスではなく)クラスから派生した場合、そのメソッドが親クラスのメソッドをオーバーライドすることもアノテーションする必要があります。
  • @SuppressWarnings アノテーションは、警告を排除できない状況下でのみ使用します。 If a warning passes this “impossible to eliminate” test, the @SuppressWarnings annotation must be used, to ensure that all warnings reflect actual problems in the code.

    When a @SuppressWarnings annotation is necessary, it must be prefixed with a TODO comment that explains the “impossible to eliminate” condition. This normally identifies an offending class that has an awkward interface. たとえば

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

    When a @SuppressWarnings annotation is required, refactor the code to isolate the software elements where the annotation applies.

Treat acronyms as words

Treat acronyms and abbreviations as words in naming variables, methods, and classes to make names more readable:

Good Bad
XmlHttpRequest XMLHTTPRequest
getCustomerId getCustomerID
class Html class HTML
String url String URL
long id long ID

As both the JDK and the Android code bases are inconsistent around acronyms, it’s virtually impossible to be consistent with the surrounding code. Therefore, always treat acronyms as words.

Use TODO comments for code that is temporary, a short-term solution, or good enough but not perfect. These comments should include the string TODO in all caps, followed by a colon:

// 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 つの異なるレベルのロギングを提供します。

  • ERRORERROR レベルで何らかのログを記録することを正当化する問題は、統計収集サーバーに報告される良い候補となります。
  • WARNINGWARNING レベルでのログ記録が正当化される問題は、統計収集サーバーに報告することも考慮されるかもしれません。
  • INFORMATIVE: 何か興味深いことが起こったことを記録するために使用します。つまり、必ずしもエラーではないものの、広範囲に影響を及ぼす可能性がある状況が検出されたときに使用します。 このような状態は、そのドメインで最も権威があると信じているモジュールによってのみ記録されるべきです (非権威のコンポーネントによって重複して記録されるのを避けるためです)。 このレベルは常にログに記録されます。
  • DEBUG: 予期しない動作を調査およびデバッグするために関連する可能性がある、デバイスで起こっていることをさらに記録するために使用します。 コンポーネントで何が起こっているかについての十分な情報を収集するために必要なものだけをログに記録します。 デバッグ ログがログを支配している場合は、冗長ロギングを使用する必要があります。

    このレベルはリリースビルドでも記録され、if (LOCAL_LOG)if LOCAL_LOGD)LOCAL_LOGif (LOCAL_LOG)if (LOCAL_LOG)if (LOCAL_LOG) ブロックの外側で行われることになるのであれば、ログの呼び出しをメソッド呼び出しにリファクタリングしないでください。

    まだ if (localLOGV) と言っているコードもあります。 これは、名前は非標準ですが、同様に許容されると考えられています。

  • VERBOSEif (LOCAL_LOGV)if (LOCAL_LOGV) ブロックの内部に表示される必要があります。

注意事項

  • VERBOSE レベル以外の与えられたモジュール内で、エラーは可能なら 1 回だけ報告されるべきです。 モジュール内の関数呼び出しの単一のチェーン内では、最も内側の関数だけがエラーを返すべきで、同じモジュールの呼び出し元は、問題の分離に著しく役立つ場合にのみ、いくつかのログを追加すべきです。
  • VERBOSEDEBUGDEBUGINFORMATIVE レベル以上のログをトリガーすべき唯一の状況は、モジュールまたはアプリがそれ自身のレベルまたは下位のレベルから来るエラーを検出するときです。
  • 通常、何らかのログを正当化する状態が何度も発生しそうな場合、同じ (または非常に類似した) 情報の多くの重複コピーでログがオーバーフローしないように、何らかの速度制限メカニズムを実装することは良い考えです。
  • ネットワーク接続の損失は一般的であると考えられ、完全に予想されることであり、喜んでログに記録されるべきではありません。 アプリ内に影響を及ぼすネットワーク接続の損失は、DEBUGVERBOSE レベルで記録されるべきです (リリース ビルドで記録されるほど深刻で予期しない結果かどうかに依存します)。
  • サード パーティ製アプリにアクセス可能なファイル システム上に完全なファイル システムを持つことは、INFORMATIVE よりも高いレベルでログに記録されるべきではないでしょう。
  • 信頼できないソース (共有ストレージ上の任意のファイル、またはネットワーク接続を介して来るデータを含む) から来る無効なデータは、予期されたものと見なされ、無効であることが検出されたときに DEBUG 以上のレベルのログをトリガーすべきではありません (その場合でもログはできる限り制限する必要があります)。
  • String+StringBuilderStringStringBuilder+Log.v() を呼び出すコードは、ログが読み込まれない場合でも、リリース ビルドで文字列の構築を含めてコンパイルおよび実行されることを覚えておいてください。
  • 他の人が読むことを意図し、リリース ビルドで利用できるようにするすべてのログは、不可解でなく簡潔であり、理解可能であるべきです。 これには、DEBUG レベルまでのすべてのロギングが含まれます。
  • 可能な場合は、ログを 1 行に収めます。 80 文字または 100 文字までの行の長さは許容されます。 可能であれば、約130または160文字 (タグの長さを含む) より長い長さは避けてください。
  • ロギングが成功を報告する場合、VERBOSE より高いレベルでは決して使用しないでください。
  • 再現が困難な問題を診断するために一時的なロギングを使用している場合、DEBUGVERBOSE レベルに保ち、コンパイル時にそれを無効にできるように if ブロックで囲みます。
  • ログによるセキュリティ リークに注意する。 プライベートな情報をログに残さないようにする。 特に、保護されたコンテンツに関する情報をログに記録することは避けてください。 これはフレームワークのコードを書くときに特に重要です。何がプライベートな情報または保護されたコンテンツになり、何がならないかを事前に知ることは簡単ではないからです。
  • System.out.println()printf()System.outSystem.err/dev/null にリダイレクトされるので、print 文は目に見える効果を持ちません。 しかし、これらの呼び出しで発生するすべての文字列構築はまだ実行されます。
  • ロギングの黄金律は、他のログがあなたのログを押し出さないように、あなたのログが不必要に他のログをバッファから押し出さないようにすることです。

Javatests スタイルのルール

テスト メソッドの命名規則に従い、テストされている特定のケースからテストされているものを分離するためにアンダースコアを使用します。 このスタイルにより、どのケースがテストされているのかがわかりやすくなります。 For example:

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

.

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です