textdrop > 翻訳 > Android コードスタイルガイド 日本語訳
ブログ > Google C++ スタイルガイド 日本語訳, Google Objective-C スタイルガイド 日本語訳, コントリビュータのためのAndroidコードスタイルガイドライン 日本語訳, Google XML文書フォーマット スタイルガイド 日本語訳
これは Android Open Source Project にある Code Style Guidelines for Contributors(2010-07-07 時点)の日本語訳です。
最終更新: 2010-07-14
ここに挙げたルールはガイドラインや推奨という位置付けではなく、厳格な決まり事です。通常、ここにあるルールに従っていない場合、Androidへのコントリビューションは受け付けられません。
既存のコードすべてがこれらのルールに従っているわけではありませんが、これから新しく書くコードはすべてこれらのルールに従うべきです。
私たちは標準のJavaコーディング規約に従いますが、いくつかルールを追加しています。
AndroidのJavaライブラリおよびツールにも規約があります。規約が大きく変更されてしまい、以前書いたコードで推奨されていないパターンやライブラリが使われてしまうかもしれません。このようなコードで作業するときには、これまでのスタイルを使い続けても構いません(一貫性を参照)。ただし、新しくコンポーネントを作る場合には、推奨されていないライブラリを使ってはいけません。
すべてのファイルのスタイルに一貫性があると、プログラムは非常にメンテナンスしやすくなります。私たちは Sun が策定した Code Conventions for the Java Programming Language に書かれている標準のJavaコーディングスタイルに、いくつか例外と追加を加えたものに従っています。Sun のスタイルガイドは広範囲にわたって詳細な説明があり、Javaコミュニティにおいて広く使われています。
この Sun のスタイルガイドに加えて、私たちは次のルールに従います。
m で始めてください。XmlHttpRequest、getUrl() など。次のように、例外を完全に無視するようなコードを書いてしまうことがあります。
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.");
}
}/** 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
}
}/** 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.
}
}例外をキャッチして何かするときに、次のように手を抜いてしまうことがよくあります。
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 をキャッチする代わりに、次のような手段をとることができます。
覚えておいてください: 例外はともだちです! 「例外をキャッチしていません」とコンパイラがエラーを出しても、嫌な顔をしてはいけません。にっこりと微笑んでください。コンパイラはコードの実行時に発生する問題をキャッチしやすくしてくれているだけなのです。
定義: ファイナライザとは、オブジェクトがガベージコレクトされるときに実行されるコードのことです。
使うのに賛成: クリーンナップするときに便利です。リソースが外部にあるときには特に便利です。
使うのに反対: ファイナライザがいつ呼び出されるか保証がありません。そもそもまったく呼び出されないことさえあります。
結論: ファイナライザを使ってはいけません。うまく例外処理することで、ファイナライザでやりたいことはたいてい可能です。どうしてもファイナライザが必要であれば、close() メソッド(のようなもの)を定義して、そのメソッドをいつ呼び出す必要があるのか、ドキュメントに明記してください。例えば、InputStream を見てください。この場合、ログがあふれそうにない限りは、ファイナライザで簡潔なログメッセージを出力しても構いませんが、必ずしもそうする必要はありません。
定義: パッケージ foo にあるクラス Bar を使いたいとき、2つのインポート方法があります。
import foo.*;import foo.Bar;#1 に賛成: インポート文の数を減らせます。
#2 に賛成: 実際にどのクラスが使われているのかが明確になります。
結論: Androidでコードをインポートするときは、必ず #2 のスタイルを使ってください。ただし、Java標準ライブラリ(java.util.*, java.io.* など)とユニットテストコード(junit.framework.*)についてはその限りではありません。
すべてのファイルの先頭には、まずコピーライト文を入れておくべきです。その後、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());
}
インポート文は次のような順序でインポートしてください。
IDE の設定とうまく合わせて、次のようにインポートすべきです。
もともとは、インポートの順序に関するスタイルはありませんでした。これは、常に IDE が順序を変えるのに任せるか、さもなくば、IDE にある自動のインポート管理機能を無効にして手動でインポートを管理しなければならない、ということを意味します。これはひどい話のように思えました。java-style が尋ねられると、望ましいスタイルはすべてここにあることでした。結局「順序をひとつ選んで一貫性をもたせる」ことが必要になりました。そこで、私たちはスタイルをひとつ選んで、スタイルガイドをアップデートし、IDEがそれに従うようにしました。私たちが期待しているのは、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;
}
中括弧で行を始めてはいけません。中括弧はその直前のコードと同じ行に置いてください。つまり、次のようになります。
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文字を超えても構いません。コピー&ペーストしやすくするためです。
例外: インポートの行はこの制限を超えても構いません。ただし、めったにないでしょう。この方がツールを書くのも簡単になります。
アノテーションは同じ言語要素にある他の修飾子よりも前に置くべきです。単純なマーカーアノテーション(例えば @Override)であれば、言語要素と同じ行に並べても置いても構いません。アノテーションが複数あったり、パラメータ付きのアノテーションの場合には、アルファベット順に1行ずつ並べるべきです。
Java 1.5 で定義された3つのアノテーションについて、Android 標準のプラクティスは以下のようになります。
その要素の利用がもはや推奨されないときには、@Deprecated アノテーションを必ず付けなければなりません。@Deprecated アノテーションを使うときには、@deprecated Javadoc タグも付けなければなりません。そして、その代わりに使う実装について説明しておくべきです。@Deprecated メソッドはまだ動作がサポートされていることも意味していることを忘れてはいけません。
もし @deprecated Javadoc タグが付いた古いコードがあれば、@Deprecated アノテーションを追加しておいてください。
メソッドがスーパークラスにある宣言や実装をオーバーライドしているときには、@Override アノテーションを必ず付けなければなりません。
例えば、もし @inheritdocs Javadoc タグを使っていて、クラス(インタフェースではなく)から派生しているときには、メソッドが親クラスのメソッドをオーバーライドしていることを示すために @Override アノテーションを付けなければなりません。
@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: 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つあります。各レベルをいつどのように使うべきか、以下に説明します。
注意: 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 文を書いても出力されて見えることはありません。それにもかかわらず、呼び出しに伴って発生する文字列構築はすべて実行されてしまうので注意してください。
注意: ログ記録には鉄則があります。あなたのログが不必要に他人のログを押し出さないようにしてください。他人のログがあなたのログを押し出さないようにするのと同じです。
テストメソッドに名前を付けるときは、アンダースコアでテスト対象と個々のテストケースを分けるのが望ましいです。こうしておくと、どのケースがテストされるのか、わかりやすくなります。
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))
}