Androidコードスタイルルール
ここに挙げたルールはガイドラインや推奨という位置付けではなく、厳格な決まり事です。そのため、 ここに挙げたルールを無視してはいけません。ただし、"need-to-use" の原則にのっとり認められた場合は、この限りではありません。
既存のコードのすべてがこれらのルールに従っているわけではありませんが、新しく書いたコードはすべてこれらのルールに従うべきです。
Java言語に関するルール
私たちは標準のJavaコーディング規約に従いますが、いくつかルールを追加しています。
- 例外: 説明なしに、例外をキャッチしながら無視してはいけません。
- 例外: 汎用の Exception をキャッチしてはいけません。ただし、スタックのルートにあるライブラリコードの場合は除きます。
- ファイナライザ: 通常、ファイナライザを使ってはいけません。
- インポート: インポートはうまく限定してください。
Javaライブラリに関するルール
AndroidのJavaライブラリおよびツールにも規約があります。時として規約が大きく変更されて、以前書いたコードが推奨されないパターンやライブラリを使うことになるかもしれません。このようなコードで作業するときには、これまでのスタイルを使い続けても構いません(一貫性を参照)。ただし、新しくコンポーネントを作る場合には、推奨されないライブラリを使ってはいけません。
Javaのスタイルに関するルール
すべてのファイルのスタイルに一貫性があると、プログラムは非常にメンテナンスしやすくなります。私たちは Sun が策定した Code Conventions for the Java Programming Language に書かれている標準のJavaコーディングスタイルに、いくつか例外と追加を加えたものに従います。Sun のスタイルガイドは幅広い範囲にわたって詳細な説明があり、Javaコミュニティでは広く使われているものです。
この Sun のスタイルガイドに加えて、私たちは次のルールに従います。
- コメント/Javadoc: コメント/Javadocを書いてください。標準のスタイルを使ってください。
- 簡潔なメソッド: 巨大なメソッドを書いてはいけません。
- フィールド: フィールドはファイルの先頭か、そのフィールドを使うメソッドの直前に書いてください。
- ローカル変数: ローカル変数のスコープを限定してください。
- インポート: android関連; サードパーティ製(アルファベット順で); java(x) という順にインポートしてください。
- インデント: インデントにはスペース4つを使ってください。タブを使ってはいけません。
- 行の長さ: 1行は100カラムまでにしてください。
- フィールド名: 非パブリック、非スタティックフィールドは
m
で始めてください。スタティックフィールドはs
で始めてください。 - 中括弧: 開き中括弧で行を始めてはいけません。
- アノテーション: 標準のアノテーションを使うこと。
- 頭字語(アクロニム)は単語: 名前では頭字語を単語として扱ってください。例えば、
XmlHttpRequest
、getUrl()
など。 - TODOのスタイル: 「TODO: こんな風に書いてください」
- 一貫性: まわりをよく見ましょう!
- ログ記録: ログを記録するときには注意してください。コストがかかります。
Javatestのスタイルに関するルール
- テストメソッドの名前付け: testMethod_specificCase という形式にするとよい。
Java言語に関するルール
例外を無視してはいけません
次のように、例外を完全に無視するようなコードを書いてしまうことがあります。void setServerPort(String value) { try { serverPort = Integer.parseInt(value); } catch (NumberFormatException e) { } }
これは絶対にやってはいけないことです。「こんなエラーが起こるわけない」とか「このエラー処理はそんなに大事ではない」と考えたのかもしれません。でも、このように例外を無視していると、いつか誰かがつまずいて、あなたのコードにある地雷を踏んでしまいます。信念をもって、コードにあるすべての Exception を処理する必要があります。どのように処理するかは、場合によって違ってきます。
空のcatch節を見たときには必ず気持ち悪いと思わなきゃいけないよ。最終的には、そうするのが正しい場合もあるだろう。たとえそうだとしても、少なくとも検討はしなきゃならない。Javaではこうした気持ち悪さから逃れることはできないよ。
例外を無視する代わりに、次のような手段をとることができます(以下、望ましい順に)。
- メソッドの呼び出し元に例外を投げます。
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.4以降でしか動きません。もし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
を見てください。この場合、ログがあふれそうにない限りは、ファイナライザで簡潔なログメッセージを出力するのはよいですが、必ずしも必要ありません。ひとつ例外があります。
X.assertTrue()
を呼び出すだけであれば、ファイナライザを書いても構いません。インポート
インポートにおけるワイルドカード
定義: パッケージ foo にあるクラス Bar を使いたいとき、2つのインポート方法があります。
import foo.*;
import foo.Bar;
#1 に賛成: インポート文の数を減らせます。
#2 に賛成: 実際にどのクラスが使われているのかが明確になります。
結論: Androidでコードをインポートするときは、必ず #2 のスタイルを使ってください。ただし、Java標準ライブラリ(java.util.*, java.io.* など)とユニットテストコード(junit.framework.*)についてはその限りではありません。
コメント/Javadoc
すべてのファイルの先頭には、まずコピーライト文を入れておくべきです。その後に、
package
文と import
文を続けましょう。各ブロックの間は1行空けておいてください。その後に、クラスやインタフェースの宣言を書きましょう。Javadoc コメントには、そのクラスやインタフェースが何をするものなのかを書いてください。/* * Copyright (C) 2007 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()); }
インポート
インポート文は次のような順序でインポートしてください。IDE の設定とうまく合うよう、次のようにインポートすべきです。
大文字は小文字よりも前にあるものと見なします。(例えば Z は a より前)。
なぜ?
もともとは、インポートの順序についてのスタイルはありませんでした。これは、常に IDE が順序を変えるのに任せるか、さもなくば、IDE にある自動のインポート管理機能を無効にして手動でインポートを管理しなければならない、ということを意味します。これはひどい話のように思えました。java-style が尋ねられると、望ましいスタイルはすべてここにあることでした。結局「順序をひとつ選んで一貫性をもたせる」ことが必要になりました。そこで、私たちはスタイルをひとつ選んで、javaguideをアップデートし、IDEがそれに従うようにしました。私たちが期待しているのは、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
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)) }