Posted at

Androidのコード規約について考えてみた

More than 5 years have passed since last update.

あくまで個人意識のため、内容はほとんど引用メイン

Android開発時のコード規約に関して、自分なりに噛み砕いて理解するために記す


コード規約の在り方

Androidでもチームで開発する際、開発メンバーが決められたコード規約に基づいて実装しないと、可読性などが失われ単体テストや結合テスト、ソースレビューにも影響が出てくる

PHPやObjective-C、もちろんJavaにもデフォルトのコード規約は存在する

Androidの時はどうすればいいのか

私の結論としては「Android Open Source Project」にある Code Style Guidelines for Contributors に従う

例えば会社の場合、ある程度その会社独自のコード規約が存在する

しかしそれは一般的でもなんでもない、私から言わせていただけば「負の遺産」にすぎない

コード規約というものは、開発者全員が共通言語として理解できるものでなくてはならない、と私は考える


Contributors(コントリビュータ)って何よ?

うれしいことに日本語訳も提供されていた コントリビュータのためのAndroidコードスタイルガイドライン 日本語訳 via textdrop

※2010-07-07 時点の日本語訳

ここでいうContributors(コントリビュータ)って何?

自分は最初「オープンソースプロジェクトのコミット権限を持っている人」と言う認識だった

この記事を書くついでに、もう一回調べてみた

例えば Apache License におけるコントリビュータの定義は次の通り


「コントリビューター」とは、ライセンサーおよびその代理を務める個人または法人で、自分のコントリビューションがライセンサーに受領されて成果物に組み込まれた者を指します。

引用元:Apache License, Version 2.0 via OSG-JP


? コントリビューション?ライセンサー?

それぞれに関しては、Apache Licenseを参考に

まぁ直訳すると「貢献者」と他のサイトにも書いてある通り、私の中では次の通りで落ち着いた


「コントリビュータ」とは、自身で作成したもの(ここでいうとAndroidのソースコード)が、その著作権元(ここでいうとOracleかGoogle)から認められた個人(または法人)のこと


こんな感じかな?


まずはJavaのコーディング規約

コントリビュータのためのAndroidコードスタイルガイドライン 日本語訳の「Java言語に関するルール」にもあるように


標準のJavaコーディング規約に従います


ここで言う「標準」というのはGoogleが出している Google Java Style ってことかな?

最初はOracleが公式として出している「Java プログラミング言語に関するコード規約」だと思ったんだけど、リンクどこいったのさ…

まぁ今回はAndroidについてのコード規約を考えるので、素直にGoogleに従う


Android コーディング規約

コントリビュータのためのAndroidコードスタイルガイドライン 日本語訳を順に追っていく


例外処理


catchが空のtry-catchは書くな!

Javaの例外としてよく書かれるtry-catch文

そもそも例外処理の中に何も書かないなら、最初からtry-catchなんて使うなと言わんばかりですが


ダメなコード例

void setServerPort(String value) {

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

もちろん、絶対ダメだよ

正しくは次の3つ


1.メソッドの呼び出し元に例外を投げるタイプ

void setServerPort(String value) throws NumberFormatException {

serverPort = Integer.parseInt(value);
}


2.抽象化した新しい例外を投げるタイプ

void setServerPort(String value) throws ConfigurationException {

try {
serverPort = Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new ConfigurationException("Port " + value + " is not valid.");
}
}


3.きちんと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 を投げるタイプ

  • 最後の手段: 自信をもって例外を無視するのが本当に適切だと言い張るタイプ

という2つがあるが、私はちゃんと上記3点のどれかに落ち着くように徹底する


Exception e とか書くな!

これ、学生時代の私でした、今となっては猛省しております


ダメなコード例

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

これをやってしまうとClassCastExceptionRuntimeExceptionといった予期せぬ例外もアプリケーションレベルにあるエラー処理でキャッチしてしまいます

ようは、なんでアプリケーションがクラッシュしたのか分からないことになってしまうからです

この場合は次のように複数のcatchで例外を捕まえましょう


良いコード例

try {

someComplicatedIOFunction(); // may throw IOException
someComplicatedParsingFunction(); // may throw ParsingException
someComplicatedSecurityFunction(); // may throw SecurityException
} catch (IOException e) {
ioExceptionError();
} catch (ParsingException e) {
parsingExceptionError();
} catch (SecurityException e) {
securityExceptionError();
}

どうしても落ちるという人は、それは実装したコードの例外パターンを知らないだけ


finalize()は使わない

ファイナライザが何?という人はコチラ

コントリビュータのためのAndroidコードスタイルガイドライン 日本語訳にも書いてあるけど「オブジェクトがガベージコレクトされるときに実行されるコード」ってこと


基本は使わない


どうしても必要なときはclose() メソッド(のようなもの)を定義するらしい

けど、そんなコードはどんな時だろう


import foo.*;のようなワイルドカードは使わない

めんどくさいときにやるよね


悪いコード例

import foo.*;


けど、これだと際にどのクラスが使われているのかが明確にならないので、やっぱりきちんと書く方が良いらしい


良いコード例

import foo.Bar;

import foo.Key;

Javaの標準ライブラリ(java.util.*, java.io.*)とか、ユニットテストコード(junit.framework.*)はOKらしいのだけど、ここもきちんと書く習慣をつけた方が望ましい


Javadocコメント

Copyrightちゃんと書こうよ

package文とimport文の順で書こうよ

各ブロックの間は1行空けて

そしたらクラスやインタフェースの宣言を書こうよ


良いコード例

/*

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


ここでも次の一文があるけど


"setFoo()" といった簡単な get/set メソッドの場合、「Fooを設定する」というだけの Javadoc を書く必要はありません


基本的には、どんなに小さいメソッドでもちゃんと書く習慣をつけることが大事

毎回Javadocを生成するイメージを持つこと


1メソッドは40行以内!

メソッドはモニターでスクロールしない大きさのスコープで作ると良いと聞いたことがある


ローカル変数のスコープは最小に

むやみやたらに変数を宣言したり、長いスコープにしない

ローカル変数の宣言はイニシャライザを入れる


importの順番を大切に

インポート文は次のような順序でインポート



  1. Android 関係のインポート

  2. サードパーティ製ライブラリ (com, junit, net, org)のインポート

  3. java および javax 関連のインポート


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



  1. 各グループ内では、アルファベット順にインポート

  2. 大文字は小文字よりも前にあるものと見なおす(例えば Z は a よりも前にある)

  3. 大きなグループ分け (android, com, junit, net, org, java, javax) の間には空行を入れておくべき



インデントはスペース4つ

関数呼び出しと引数を含んだ行の折り返しは、スペース8つでインデント


良いコード例

Instrument i =

someLongExpression(that, wouldNotFit, on, one, line);
12345678

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


悪いコード例

Instrument i =

someLongExpression(that, wouldNotFit, on, one, line);
1234


フィールド名


非パブリック、非スタティックフィールドの名前は m スタート


良いコード例

public class MyClass {

int mPackagePrivate;
private int mPrivate;
protected int mProtected;
}


スタティックフィールドの名前は s スタート


良いコード例

public class MyClass {

private static MyClass sSingleton;
}


それ以外はフィールドは小文字スタート


良いコード例

public class MyClass {

public int publicField;
}


パブリックな static final フィールド(定数)はアンダースコアを含む大文字だけ


良いコード例

public class MyClass {

public static final int SOME_CONSTANT = 42;
}


中括弧 {} で行は初めてはダメ

これはよくあるコードスタイル戦争


悪いコード例

class MyClass

{ // ×
int func() {
if (something) {
// ...
} else if (somethingElse) {
// ...
} else
body(); // ×
}
}

条件文にも中括弧を付ける


ただし、条件文全体(条件と本体)が1行に収まるのであれば、中括弧を付けなくても構いません


とあるけど、ちゃんと統一したほうが良い気がする


良いコード例

class MyClass {

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


1行の長さは100文字まで

これよく80文字を基準にしている所も多いはず、私もそう

ここに関しては、コントリビュータのためのAndroidコードスタイルガイドライン 日本語訳にもあるようにある程度の例外は受け入れるのが吉


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

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



Javaのアノテーションはきちんと書く

これはそうだね、ちゃんと書こう

それぞれのアノテーションの説明は省く


@Deprecated

かならず付けろ


@Override

絶対書け


@SuppressWarnings

こちらに関しては、これを使わないようにコードをきちんと分離したほうがいいね


名前における頭字語(アクロニム)

こちらは、たまにクラス名とかで気持ち悪くてもがんばる

よい
わるい

XmlHttpRequest
XMLHTTPRequest

getCustomerId
getCustomerID

頭字語や略語そのものが名前の場合(HTMLなど)にも、このスタイルを適用する

よい
わるい

class Html
class HTML

String url;
String URL;

long id;
long ID;


TODOを書く場合には具体的な日付も入れよう


良いコード例

// TODO: ここは数値における仕様が決定する2014/12/31までに修正



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

こちらに関しては、使用するツール(JUnitであったり)によって、きちんと決めましょう

大抵は、メソッド名にtest_みたいな統一ルールになるはず


まとめ

他にもJavaに関するコーディング規約 Google Java Style も再度目を通す必要があると思った

そちらはまた今度