コントリビュータのためのAndroidコードスタイルガイドライン 日本語訳

ここに挙げたルールはガイドラインや推奨という位置付けではなく、厳格な決まり事です。通常、ここにあるルールに従っていない場合、Androidへのコントリビューションは受け付けられません。

既存のコードすべてがこれらのルールに従っているわけではありませんが、これから新しく書くコードはすべてこれらのルールに従うべきです。

Java言語に関するルール

私たちは標準のJavaコーディング規約に従いますが、いくつかルールを追加しています。

  1. 例外: 説明なしに、例外をキャッチしながら無視してはいけません。
  2. 例外: 汎用の Exception をキャッチしてはいけません。ただし、スタックのルートにあるライブラリコードの場合は除きます。
  3. ファイナライザ: 通常はファイナライザを使ってはいけません。
  4. インポート: インポートはうまく限定してください。

Javaライブラリに関するルール

AndroidのJavaライブラリおよびツールにも規約があります。規約が大きく変更されてしまい、以前書いたコードで推奨されていないパターンやライブラリが使われてしまうかもしれません。このようなコードで作業するときには、これまでのスタイルを使い続けても構いません(一貫性を参照)。ただし、新しくコンポーネントを作る場合には、推奨されていないライブラリを使ってはいけません。

Javaのスタイルに関するルール

すべてのファイルのスタイルに一貫性があると、プログラムは非常にメンテナンスしやすくなります。私たちは Sun が策定した Code Conventions for the Java Programming Language に書かれている標準のJavaコーディングスタイルに、いくつか例外と追加を加えたものに従っています。Sun のスタイルガイドは広範囲にわたって詳細な説明があり、Javaコミュニティにおいて広く使われています。

この Sun のスタイルガイドに加えて、私たちは次のルールに従います。

  1. コメント/Javadoc: コメント/Javadocを書いてください。そこでは標準のスタイルを使ってください。
  2. 簡潔なメソッド: 巨大なメソッドを書いてはいけません。
  3. フィールド: フィールドはファイルの先頭か、フィールドを使うメソッドの直前に書いてください。
  4. ローカル変数: ローカル変数のスコープは限定するようにしてください。
  5. インポート: android関連; サードパーティ製(アルファベット順); java(x) という順序でインポートしてください。
  6. インデント: インデントにはスペース4つを使ってください。タブを使ってはいけません。
  7. 行の長さ: 1行は100カラムまでにしてください。
  8. フィールド名: 非パブリック、非スタティックフィールドは m で始めてください。
  9. 中括弧: 開き中括弧で行を始めてはいけません。
  10. アノテーション: 標準のアノテーションを使ってください。
  11. 頭字語(アクロニム)は単語: 名前では頭字語を単語として扱ってください。例えば、XmlHttpRequestgetUrl() など。
  12. TODOのスタイル: 「TODO: こんな風に書いてください」
  13. 一貫性: まわりをよく見てください!
  14. ログ記録: ログを記録するときには注意してください。ログにもコストがかかるのです。

Javatestのスタイルに関するルール

  1. テストメソッドの名前付け: testMethod_specificCase という形式にするとよいでしょう。

Java言語に関するルール

例外を無視してはいけません

次のように、例外を完全に無視するようなコードを書いてしまうことがあります。

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

これは絶対にやってはいけないことです。「こんなエラーが起こるわけない」とか「このエラー処理はそんなに大事なことじゃない」と考えたのかもしれません。しかし、このように例外を無視していると、いつか誰かがつまずいて、あなたのコードにある地雷を踏んでしまいます。信念をもって、コードにあるすべての Exception を処理しなければなりません。どのように処理するかは、場合により異なります。

空の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 
        }
    }
  • Exception をキャッチして、新しい 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);
        }
    }
    元の例外が RuntimeException のコンストラクタの引数に渡されていることに注意してください。もしJava 1.3でもコンパイルする必要があるなら、元の例外を省略する必要があります。
  • 最後の手段: 自信をもって例外を無視するのが本当に適切だと言えるのであれば、例外を無視しても構いません。ただし、なぜそうするのか、きちんとした理由をコメントに入れておく必要があります。
    /** 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.
        }
    }

汎用の Exception をキャッチしてはいけません

例外をキャッチして何かするときに、次のように手を抜いてしまうことがよくあります。

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

これはやるべきではありません。ほとんどの場合、汎用の Exception や Throwable をキャッチするのは適切ではありません。しかも、できるだけ Throwable を使わない方が望ましいです。なぜなら、Throwable には Error 例外も含まれるためです。これは非常に危険です。予期せぬ例外(ClassCastException といった RuntimeException も含まれます)が起こったときも、最終的にアプリケーションレベルにあるエラー処理でキャッチしてしまいます。こうしてしまうと、誰がその失敗に対処するのかわかりにくくなります。また、もし誰かがあなたの呼び出しているコードに新しい型の Exception を追加しても、コンパイラは別のエラー処理が必要になったことを教えてはくれません。ほとんどの場合、例外のタイプが違うのに同じ方法で処理しようとするべきではありません。

めったにないことですが、このルールには例外があります。テストコードやトップレベルにあるコードの場合、あらゆるタイプのエラーをキャッチしたいことがあります。エラーがUI上に現れるのを防いだり、バッチ処理を継続するためです。このような場合には、汎用の Exception(あるいは Throwable)をキャッチして、エラーを適切に処理しても構いません。ただし、その前によく注意して考えてください。そして、なぜそうしても安全なのか、理由を説明するためのコメントを付けておくべきです。

汎用の Exception をキャッチする代わりに、次のような手段をとることができます。

  • try の後、例外ごとに別の catch ブロックでキャッチする。これは不恰好に見えるかもしれませんが、すべての Exception をキャッチするよりましです。catch ブロックでは、同じコードを繰り返しすぎないよう注意してください。
  • 複数の try ブロックを使って、きめ細かくエラー処理するようリファクタリングする。パースから IO を分離して、それぞれ個別にエラー処理するようにしてください。
  • 例外を投げ直す。どうしてもこのレベルで例外をキャッチする必要がない多くの場合には、単にそのメソッドに例外を投げさせてください。

覚えておいてください: 例外はともだちです! 「例外をキャッチしていません」とコンパイラがエラーを出しても、嫌な顔をしてはいけません。にっこりと微笑んでください。コンパイラはコードの実行時に発生する問題をキャッチしやすくしてくれているだけなのです。

ファイナライザ

定義: ファイナライザとは、オブジェクトがガベージコレクトされるときに実行されるコードのことです。

使うのに賛成: クリーンナップするときに便利です。リソースが外部にあるときには特に便利です。

使うのに反対: ファイナライザがいつ呼び出されるか保証がありません。そもそもまったく呼び出されないことさえあります。

結論: ファイナライザを使ってはいけません。うまく例外処理することで、ファイナライザでやりたいことはたいてい可能です。どうしてもファイナライザが必要であれば、close() メソッド(のようなもの)を定義して、そのメソッドをいつ呼び出す必要があるのか、ドキュメントに明記してください。例えば、InputStream を見てください。この場合、ログがあふれそうにない限りは、ファイナライザで簡潔なログメッセージを出力しても構いませんが、必ずしもそうする必要はありません。

インポート

インポートにおけるワイルドカード

定義: パッケージ foo にあるクラス Bar を使いたいとき、2つのインポート方法があります。

  1. import foo.*;
  2. import foo.Bar;

#1 に賛成: インポート文の数を減らせます。

#2 に賛成: 実際にどのクラスが使われているのかが明確になります。

結論: Androidでコードをインポートするときは、必ず #2 のスタイルを使ってください。ただし、Java標準ライブラリ(java.util.*, java.io.* など)とユニットテストコード(junit.framework.*)についてはその限りではありません。

コメント/Javadoc

すべてのファイルの先頭には、まずコピーライト文を入れておくべきです。その後、package 文と import 文を続けてください。各ブロックの間は1行空けておいてください。その後、クラスやインタフェースの宣言を書いてください。そのクラスやインタフェースが何をするものなのか、Javadoc コメントに書いておいてください。

/*
 * Copyright (C) 2010 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 {
    ...
}

クラスと複雑なパブリックメソッドにはすべて、少なくともそのクラスやメソッドが何をするものか説明する一文を 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) {
}

"setFoo()" といった簡単な get/set メソッドの場合、「Fooを設定する」というだけの Javadoc を書く必要はありません。メソッドがもっと複雑な場合(何らかの制約があったり、重大な副作用がある場合など)には、それらについて明記しておかなくてはなりません。また、プロパティ "Foo" が何を意味しているのかわかりにくければ、それも説明しておくべきです。

コードを書くときには、そのメソッドがパブリックであろうとなかろうと、すべてのメソッドに Javadoc を書いておくと役立ちます。パブリックメソッドは API の一部なので、Javadoc を書かなくてはいけません。

現在のところ、Android では Javadoc コメントの書き方について何も強制していませんが、Sun Javadoc conventions従うべきです。

簡潔なメソッド

メソッドはできるだけ小さく、ポイントを絞ったものにするべきです。ただし、長いメソッドの方がふさわしい場合もあります。そのため、メソッドの長さには厳しい制限を設けていません。もしメソッドが40行を超えるようであれば、プログラムの構造に影響を与えることなく分割できないか検討してください。

ローカル変数

ローカル変数のスコープは最小限にするべきです(Effective Java Item 29を参照)。そうすることで、コードは読みやすくメンテナンスしやすくなり、間違いも起きにくくなります。変数は、その変数が使われている箇所をすべて含むブロックのうち、いちばん内側にあるブロックで宣言すべきです。

ローカル変数は初めて使うところで宣言すべきです。ほとんどの場合、ローカル変数の宣言はイニシャライザに入れておくべきです。その時点で変数を初期化するのに十分な情報がない場合には、その情報が得られてから宣言すべきです。

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

インポート

インポート文は次のような順序でインポートしてください。

  1. Android 関係のインポート
  2. サードパーティ製ライブラリ (com, junit, net, org)のインポート
  3. java および javax 関連のインポート

IDE の設定とうまく合わせて、次のようにインポートすべきです。

  • 各グループ内では、アルファベット順にインポートします。
  • 大文字は小文字よりも前にあるものと見なします。(例えば Z は a よりも前にある)。
  • 大きなグループ分け (android, com, junit, net, org, java, javax) の間には空行を入れておくべきです。

なぜ?

もともとは、インポートの順序に関するスタイルはありませんでした。これは、常に IDE が順序を変えるのに任せるか、さもなくば、IDE にある自動のインポート管理機能を無効にして手動でインポートを管理しなければならない、ということを意味します。これはひどい話のように思えました。java-style が尋ねられると、望ましいスタイルはすべてここにあることでした。結局「順序をひとつ選んで一貫性をもたせる」ことが必要になりました。そこで、私たちはスタイルをひとつ選んで、スタイルガイドをアップデートし、IDEがそれに従うようにしました。私たちが期待しているのは、IDEのユーザがコードを書いているうちに、余計なエンジニアリング作業をせずとも、すべてのパッケージがこのパターンに沿ってインポートされることです。

私たちが選んだスタイルは次のようなものです。

  • 最初に調べたいインポートを先頭に置くこと (android)
  • 最後に調べたいインポートを最後に置くこと (java)
  • 人間というものは簡単にスタイルに従えるものです
  • IDE はスタイルに従えます

スタティックインポートをどうするか?

スタティックインポートを使うか、また、どこに置くのかについては、少し議論になりました。インポートにスタティックインポートをちりばめるのが好みの人もいれば、他のすべてのインポートの上または下にまとめて置くのが好みの人もいます。しかも、すべての IDE で同じ順序になるような方法はまだありません。

ほとんどの人がこれを優先度の低い問題だと考えているので、各自の判断にまかせることにしました。ただし、一貫性をもたせるようにしてください。

インデント

ブロックのインデントにはスペース4つを使ってください。タブを使ってはいけません。迷ったときには、まわりにあるコードを見て一貫性をもたせてください。

関数呼び出しと引数を含んだ行の折り返しは、スペース8つでインデントしてください。例えば、これは正しいインデントです。

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

これは正しくないインデントです。

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

フィールド名

  • 非パブリック、非スタティックフィールドの名前は m で始めてください。
  • スタティックフィールドの名前は 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;
}

中括弧

中括弧で行を始めてはいけません。中括弧はその直前のコードと同じ行に置いてください。つまり、次のようになります。

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

条件文には中括弧を付けてください。ただし、条件文全体(条件と本体)が1行に収まるのであれば、中括弧を付けなくても構いません(そうしなければならないわけではありません)。つまり、これも許されます。

if (condition) {
    body(); // ok 
}
if (condition) body(); // ok

しかし、これは許されません。

if (condition)
    body(); // bad

行の長さ

コードにおけるテキスト行は、せいぜい100文字までにするべきです。

このルールについてはかなり議論があったのですが、結局は最長100文字のままとなりました。

例外: コメント行に100文字以上のコマンド例やURL文字列が含まれる場合には、100文字を超えても構いません。コピー&ペーストしやすくするためです。

例外: インポートの行はこの制限を超えても構いません。ただし、めったにないでしょう。この方がツールを書くのも簡単になります。

Java 1.5 アノテーション

アノテーションは同じ言語要素にある他の修飾子よりも前に置くべきです。単純なマーカーアノテーション(例えば @Override)であれば、言語要素と同じ行に並べても置いても構いません。アノテーションが複数あったり、パラメータ付きのアノテーションの場合には、アルファベット順に1行ずつ並べるべきです。

Java 1.5 で定義された3つのアノテーションについて、Android 標準のプラクティスは以下のようになります。

@Deprecated

その要素の利用がもはや推奨されないときには、@Deprecated アノテーションを必ず付けなければなりません。@Deprecated アノテーションを使うときには、@deprecated Javadoc タグも付けなければなりません。そして、その代わりに使う実装について説明しておくべきです。@Deprecated メソッドはまだ動作がサポートされていることも意味していることを忘れてはいけません。

もし @deprecated Javadoc タグが付いた古いコードがあれば、@Deprecated アノテーションを追加しておいてください。

@Override

メソッドがスーパークラスにある宣言や実装をオーバーライドしているときには、@Override アノテーションを必ず付けなければなりません。

例えば、もし @inheritdocs Javadoc タグを使っていて、クラス(インタフェースではなく)から派生しているときには、メソッドが親クラスのメソッドをオーバーライドしていることを示すために @Override アノテーションを付けなければなりません。

@SuppressWarnings

@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 のコードベースはどちらも、まったくと言っていいほど一貫性がありません。したがって、まわりのコードと一貫性をもたせるのは事実上不可能です。それでもグッとこらえて、どうか頭字語を単語として扱ってください。

このスタイルルールについて、もっと理由が必要であれば、Effective Java Item 38 と Java Puzzlers Number 68 を読んでください。

TODO のスタイル

コード中の 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を理解できるようになれば、このコードを削除する」など)を確認してください。

一貫性

最終的に考えること: 一貫性をもたせてください。コードを書いているなら、数分かけてまわりのコードを調べて、スタイルを決めてください。もし if 節のまわりに空白があれば、あなたもそうすべきです。もしスターで囲まれた小さなボックス型のコメントが使われていれば、あなたのコメントも同じようにしてください。

スタイルガイドラインがあるということは、コーディングに共通のボキャブラリがあるということです。これがあるおかげで、どうやって話すのかではなく、何を話すのかに全員が集中できるようになります。このガイドラインでは、全般的なスタイルに関するルールを説明しました。これでボキャブラリについてはわかったはずです。ただし、ローカルなスタイルも重要です。あなたの追加したコードが、まわりにある既存のコードと全然違って見えてしまうと、コードを読む人は不連続さを感じて、リズムが狂ってしまいます。これは避けてください。

ログ記録

ログ記録というのは必要不可欠なものですが、適度に簡潔にしておかないとパフォーマンスにかなり悪影響を与えて、すぐに役に立たなくなります。ログ記録ファシリティにはレベルが5つあります。各レベルをいつどのように使うべきか、以下に説明します。

  • ERROR: このレベルは何か致命的なことが発生したときに使うべきです。つまり、ユーザにとって目に見えて重大で、データの削除やアプリケーションのアンインストール、データパーティションの削除、電話全体の再起動(あるいは、もっともっと悪いこと)といったことを明確にしないと復旧不能な問題が発生したときです。このレベルは必ずログに記録しておいてください。 通常、ERROR レベルでログ記録するような問題は、統計収集サーバに報告するのが望ましいです。
  • WARNING: このレベルは深刻な予期せぬことが発生したときに使うべきです。つまり、ユーザにとって目に見えて重大ですが、特定のアクションを実行すればデータを失うことなく復旧可能な問題が発生したときです。このアクションには、しばらく待つだけやアプリを再起動することから、アプリケーションの新しいバージョンを再ダウンロードしたり、デバイスをリブートすることまで、いろいろあります。このレベルは必ずログに記録しておいてください。 WARNING レベルでログ記録するような問題も、統計収集サーバへの報告を検討するのが望ましいです。
  • INFORMATIVE: このレベルは多くの人にとって関心のあることが発生したとき、それを通知するために使うべきです。つまり、広範囲な影響があるかもしれませんが、必ずしもエラーではないような状況です。これは、そのドメインにおいて最も信頼できると思われるモジュールによってのみログ記録されるべきです。(信頼できないコンポーネントによって、ログが二重に記録されるのを防ぐため)。このレベルは必ずログに記録しておいてください。
  • DEBUG: このレベルはデバイスに何が発生しているのか、想定外の動作を調査、デバッグするための情報を記録するときに使うべきです。あなたのコンポーネントに何が起こっているのか、必要十分な情報だけをログに記録すべきです。 もしデバッグログがログの大部分を占めるようならば、それらは VERBOSE レベルで記録すべきです。DEBUG レベルはリリースビルドでも記録されるため、if (LOCAL_LOG) ブロックや if (LOCAL_LOGD) ブロックで囲んでおく必要があります。この LOCAL_LOG[D] はクラスやサブコンポーネントで定義され、こうしたログをすべて無効にできるようになっています。 したがって、if (LOCAL_LOG) ブロックにはアクティブな論理を入れてはいけません。ログに必要な文字列構築も if (LOCAL_LOG) ブロックのなかに入れておく必要があります。もし if (LOCAL_LOG) ブロックの外側で文字列構築しようとしているなら、ログ記録の呼び出しをメソッド呼び出しにリファクタリングしてはいけません。依然として if (localLOGV) を使っているコードもあります。これもまだ使えるのですが、名前付けは Android の標準に従っていません。
  • VERBOSE: 以上のレベルのいずれにも当てはまらないログはすべて、VERBOSE レベルを使うべきです。このレベルはデバッグビルドでのみログが記録されるよう、if (LOCAL_LOGV) ブロック(あるいは同等のもの)で囲んでおくべきです。こうすることで、デフォルトではコンパイルされなくなります。ログに必要な文字列構築は、リリースビルドに入らないよう、if (LOCAL_LOGV) ブロックのなかに入れておく必要があります。

注意: VERBOSE レベルを除いて、できる限り、エラー報告はモジュール内で一度だけにするべきです。モジュール内の一連の関数呼び出しにおいては、最も内側にある関数だけがエラーを返すべきです。そして、同一モジュールにある呼び出し元は、問題を切り分けるのに非常に有益なときにのみ、ログを追記すべきです。

注意: 複数のモジュールが連携している場合、上位レベルのモジュールからやってきた不正なデータを、下位レベルのモジュールで検出することがあります。このときには、VERBOSE レベルを除いて、下位レベルのモジュールでのみ、その状況を DEBUG ログに記録すべきです。しかも、呼び出し元からはわからない情報がある場合にのみ、ログ記録してください。具体的には、例外が投げられたり(例外には関連する情報がすべて含まれているはずです)、ログ記録する唯一の情報がエラーコードに含まれている場合には、ログに記録する必要はありません。これはフレームワークとアプリケーション間のやりとりにおいて、特に重要です。サードパーティ製アプリケーションに原因があって、それをフレームワークがうまく処理する場合には、DEBUG レベルよりも高いレベルでログを記録すべきではありません。モジュールやアプリケーションにおいては、同レベルあるいは下位レベルからやってきたエラーを検出した場合にのみ、INFORMATIVE レベル以上でログ記録してください。

注意: 通常でも、ログを記録する必要のある状況が何度も発生する可能性があるなら、同じ(あるいはよく似た)情報でログがあふれないよう、ログ記録を制限する仕組みを実装しておいてください。

注意: ネットワークの切断はよくあることなので、あらかじめ十分想定しておいてください。特別な理由がない限り、これをログに記録すべきではありません。アプリケーションに重大な影響を及ぼすようなネットワークの切断は、DEBUG レベルか VERBOSE レベルでログ記録しておくべきです(リリースビルドでもログ記録すべきかどうかは、結果がどれだけ深刻で想定外なのかによります)。

注意: ファイルシステムへのアクセス、あるいはサードパーティ製アプリケーションの動作によって、ファイルシステムがいっぱいになったとき、その状況を INFORMATIVE レベル以上でログ記録すべきではありません。

注意: 信頼できないソース(共有ストレージにあるファイルやネットワーク経由のデータも含まれます)から不正なデータがやって来ることは十分想定されることなので、たとえ不正なデータを検出しても、DEBUG レベルよりも高いレベルでログ記録すべきではありません。(ログ記録はできるだけ限定すべきです)。

注意: String オブジェクトに対して '+' 演算子を使うと、知らないうちにデフォルトのバッファサイズ(16文字)をもつ StringBuilder オブジェクトと、わずかな一時的な String オブジェクトが生成されているということを覚えておいてください。 つまり、デフォルトの '+' 演算子に任せるよりも、明示的に StringBuffer オブジェクトを生成した方がコストがかからない(しかも、実際のところ、ずっと効率がよくなる)ということです。また、Log.v() の呼び出しは、リリースビルドでもコンパイルされて実行されることを覚えておいてください。たとえログが読めなくても、文字列構築は実行されてしまいます。

注意: 他人に読んでもらうようなリリースビルドでも有効なログは、曖昧さのない簡潔なものにすべきです。また、非常に理解しやすいものにすべきです。これは DEBUG レベルまでのすべてのログ記録に当てはまります。

注意: ログ記録は意味が通じる限り、1行に収めるべきです。1行が80文字から100文字であれば、まったく問題ありません。(タグの長さも入れて)130文字あるいは160文字を超えるのは、できる限り避けるべきです。

注意: 成功したことをログに記録するときには、VERBOSE レベルよりも高いレベルにしてはいけません。

注意: 再現困難な問題を調査するために一時的にログを記録するときには、DEBUG もしくは VERBOSE レベルにするべきです。そして、if ブロックで囲んで、コンパイル時にすべて無効にできるようにしておくべきです。

注意: ログ全体を通して、情報漏洩には注意してください。プライベートな情報をログに記録するのは避けるべきです。保護されている内容に関する情報は、絶対に記録してはいけません。フレームワークのコードを書いているときには、これは特に重要です。フレームワークのコードにおいて、ある情報がプライベートもしくは保護されている内容であるか否かを事前に知るのは容易ではないためです。

注意: System.out.println()(またはネイティブコードの printf())を使ってはいけません。System.out と System.err は /dev/null にリダイレクトされます。したがって、いくら print 文を書いても出力されて見えることはありません。それにもかかわらず、呼び出しに伴って発生する文字列構築はすべて実行されてしまうので注意してください。

注意: ログ記録には鉄則があります。あなたのログが不必要に他人のログを押し出さないようにしてください。他人のログがあなたのログを押し出さないようにするのと同じです。

Javatestのスタイルに関するルール

テストメソッドの名前付け

テストメソッドに名前を付けるときは、アンダースコアでテスト対象と個々のテストケースを分けるのが望ましいです。こうしておくと、どのケースがテストされるのか、わかりやすくなります。

例えば、

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))
}
↑ 先頭に戻る
QLOOKアクセス解析