戻る

Simple, Open, Share

Home
トップページ 会社案内 Javaコーディング標準 開発系メモ Q&A

4、 ガイドライン

(28) コーディングスタイル

コーディングスタイルは,Sun Microsystems, Inc の JDK ソースに準じる.
インデンテ ーションは基本的にK&R のC 言語スタイルと同じだが,クラスおよびメソッドの定義開 始の”{“を改行せずに書く.

別法: インデント,{} の位置については,各自の創造性を阻害することのないよう,敢え て規定しない.

(29) 長い行

一行は最大 80 桁とし,それを超える場合は行を分割する.

分割の指針は,
(1) ローカル変 数を利用,
(2)カンマで改行,
(3)優先度の低い演算子の前で改行,とする.

例:
double length = Math.sqrt(Math.pow(Math.random(), 2.0) +
Math.pow(Math.random(), 2.0));

// 方針(1)
double xSquared = Math.pow(Math.random(), 2.0);
double ySquared = Math.pow(Math.random(), 2.0);
double length = Math.sqrt(xSquared + ySquared);

// 方針(2)
double length = Math.sqrt(Math.pow(Math.random(), 2.0,
Math.pow(Math.random(), 2.0);

// 方針(3)
return this == obj
||(this.obj instanceof MyClass
&& this.field == obj.field);

(30) 長い宣言行

クラス,メソッドの宣言が長い場合,
(1)extends/implements/throws 節で改行,
(2) カン マで改行とする.

例: Copyright (c) 2000-2006 Eiwa System Management, Inc. Object Club Kenji Hiranabe 15 public class LongNameClassImplemenation extends AbstractImplementation, implements Serializable, Cloneable { private void longNameInternalIOMethod(int a, int b) throws IOException { // … } public void longMethodSignature(int a, int b, int c, int d, int e, int f) { // … } // … }

(31) import

import では,* をなるべく使わない.
同じパッケージから3つ以上のクラスをimport する場合は,* を使う.

理由: 依存性を明確化.多くの*を使ったimport があると,読み手が苦労する.
ただし, あるパッケージをヘビーに使用する場合などは.
バランスから*を使った方が見やすい.

(32) abstract class vs. interface

抽象クラス(abstract class)はなるべく使わず,interface を多用せよ.
abstract class は, 一部実装があり,一部抽象メソッドであるような場合にのみ使うこと.

理由: interface は幾つでも継承できるが,class は1 つだけ.
1 つから継承してしまうと, もう継承できずもったいない.

(33) public variable

インスタンス変数は,極力public にせず,妥当なアクセスメソッドを設けること.

理由:
オブジェクト指向の標準.クラスの内部状態に勝手にアクセスさせるのはよくない.
ただし,以下の条件をすべて満たす場合,インスタンス変数をpublic にし,直接アクセス させてもよい.

(34) 初期化

初期化をあてにしない(参照がnull に初期化されているとか).
また,2度初期化しない.

悪い例:
class PoorInitialization {
private name = “initial_name”;
public Sample() {
name = “initial_name”;
}

理由: 初期化に関するバグを最小化する.

(35) static 変数を避ける

static 変数(クラス変数)は極力避ける.
(static final 定数は除く)

理由: static 変数は,セミグローバルと言って良い.
より文脈依存なコードを招き,副作 用を覆いかくしてしまう.

(36) final を好め

もしインスタンス変数が,作成されたあとに決して変化しないなら,final を積極的に使 用する.
また,メソッドの引数の参照先を変更しないなら,final とせよ.

理由:
final はsynchronization やコンパイルの効率化などが適用されやすい.
内部クラ スから引数を参照する場合は,final である必要がある.

(37) private vs. protected

private よりは,protected を使用すること.

理由:
private は確実にそのクラス外からの使用をシャットアウトできるが,
クライアン トが,より細かいチューニングをsubclass 化によって行うことを出来なくしてしまう.

別法:
private をより好んで使え.
protected にしてしまうと以降,変更が起ったとき にそれを継承している全クラスに影響が出てしまう.

(38) get/set メソッド

無闇にインスタンス変数へのアクセスメソッド getX()/setX() を作成して public に することは避ける.
その必要性を検討し,もっと意味のあるメソッドにする.

理由:
インスタンス変数は,他のインスタンス変数に依存していることが多い.
クラス内 部の整合性を崩してはならない.

(39) 変数隠し

スーパークラスの変数名と,同じ変数名を使う事は避けよ.

理由:
一般的にはこれはバグである.
もし意図があるならコメントせよ.

(40) 配列宣言

配列の宣言は,Type[] arrayName とせよ.

理由:
Type arrayName[] は C からの名残として残っているに過ぎない.

例:
static void main(String[] args); --- ○
static void main(String args[]); --- ×

(41) public メソッド

クラスの public メソッドは,「自動販売機のインターフェイス」を目標に.分かりやす く,使いかたを間違っても内部の整合性はこわれないように設計する.
また,可能ならば 契約による設計(Design by Contract)を行い,クラスの不変条件と共にメソッドの事前・事 後条件をコードで表現せよ.

(42) 状態取得と状態変更の分離

メソッドは,「1 つの事」を行うように設計せよ.
特に,状態変更と状態取得の2つのサー ビスを1つのメソッドで行わない.
状態を変更するメソッドのreturn 値はvoid にせよ.

Stack の例では, top() と removeTop() の 2 つの方が,pop() より良い.

理由 1:
1つの事を行うメソッドの方が分かりやすい(Stack の例は,慣用の方が強い為, pop() が好まれるだけ).

理由 2:
並行性の制御,例外の安全保証がしやすい
(参考: C++では,pop()メソッドが例外 安全に出来ない理由で,標準ライブラリではpop()は値を返さない仕様となっている).

理由 3:
サブクラス化による拡張がしやすい.

(43) this のreturn

クライアントの便宜を考えたつもりでも,this をreturn することはなるべく避ける.

理由:
a.meth1().meth2().meth3() というような連鎖は,一般的に synchronization 上の問題の元になる.

(44) メソッドの多重定義

引数のタイプによるメソッドのオーバーロードはなるべく避ける
(引数の数が違うものは OK である).

特に,継承と絡むと厄介である.

例:
× : draw(Line), draw(Rectangle)
○ : drawLine(Line), drawRectangle(Rectangle) ○ : draw(Shape)

(45) equals()とhashCode()

Object.equals()メソッドをオーバーライドするなら,同時にhashCode()メソッドも オーバーライドせよ.
逆も同じ.

理由:
コンテナクラス(Hashtable)などに対応するため.

(46) clone()

もし,clone() メソッドが使われるようなら,Cloneable を実装し明示的にそれを書 くこと.

例:
class Foo implements Cloneable {
// ...
public Object clone() {
try {
Foo foo = (Foo)super.clone(); // Foo クラスの属性のクローン
// ...
} catch (CloneNotSupportedException e) {
// Cloneable を implements しているのだから 起り得ない
throw new InternalError();
}
}
}

理由:
shallow copy ではよくないケースがほとんどである.

(47) デフォルトコンストラクタ

可能ならいつでもデフォルトのコンストラクタ(引数がないもの)を用意せよ.

理由:
Class.newInstance() でクラス名文字列からダイナミックにそのクラスを作成可 能.

(48) abstract method in abstract classes

abstract クラスでは,no-op のメソッドを書くより,明示的にabstact メソッドと宣言 せよ.
また,共有可能なデフォルトの実装を用意できるなら,それをprotected とし, サブクラスが1 行で処理を書けるようにせよ.

理由:
java コンパイラは,コンパイル時に実装されていないabstract メソッドを検出でき るため,単に実装を忘れていただけ,というバグを回避できる.

(49) オブジェクトの同値比較

オブジェクトの比較では equals()メソッドを使い,== を使うな.
特に, String の比 較では== を使用してはならない.

理由:
もし実装者がequals()を用意しているなら,それを使ってほしくて実装したはず.
equals()のデフォルトの実装は,単なる== である.

理由:
ユニットテストでは,assertEquals が equals() を利用しているため,簡単に 同値テストが書ける.

(50) 宣言と初期化

ローカル変数は,初期値と共に宣言せよ.

理由:
変数の値に関する仮定を最小化する.

悪い例:
void f(int start) {
int i, j; // 初期値なしの宣言
// 多くのコード
// ...
i = start + 1; j = i + 1;
// i, j を使う }

良い例:
void f(int start) {
// 多くのコード
// ...
// 使う前,はじめて宣言と初期化
int i = start + 1;
int j = i + 1;
// i, j を使う }

(51) ローカル変数の再利用は悪

ローカル変数を使い回しするより,新しいものを宣言して初期化せよ.

理由:
変数の値に関する仮定を最小化する.

理由:
コンパイラの最適化を助ける.

悪い例:
void f(int N, int delta) {
int i; // 初期値なしの宣言
for (i = 0; i < N; i++) {
// i を使う
}
for (i = 0; i < N; i++) {
// またi を使う
if (...) {
break;
}
}
if (i != N) {
// 最後まで回ったかの判定にi を使っている
// ...
}
i = N – delta*2; // またまた再利用
// ...
}

良い例:
void f(int N, int delta) {
for (int i = 0; i < N; i++) {
// i を使う
}
for (int i = 0; i < N; i++) {
// 別のi を使う
if (...) {
found = true;
break;
}
}
if (found) {
// ...
}
int total = N – delta*2;
// 別の意味ある変数
// ...
}

(52) if/while 条件中の “=”

if, while の条件には,代入 “=” を使ってはならない.

理由:
ほとんどの場合,バグである.
java コンパイラは,boolean 型でないかぎり,この バグをキャッチできるが.

(53) 大小比較演算子

“<”, “<=”を好んで使い,”>”, “>=”はなるべく避けよ.

理由:
大小の方向を統一し,右側を大きい方にすることで,混乱を避ける.

(54) キャスト

キャストは,できる限り instanceof の条件文で囲め.
C cx = null; if (x instanceof C) cx = (C)x; else evasiveAction();

理由:
これで,「オブジェクトがそのインスタンスじゃなかったら?」とういことを常に考 える癖がつく.
ただし,キャスト出来ない場合がバグである,と判断できる場合は,この 限りではない.

(55) 例外クラス

例外クラスは大域的な性格をもち,多用するとプログラムの流れを読みにくくしてしまう ことを認識する.
例外クラスは,新たに作成するよりも,JDK 標準パッケージに含まれているものを利用で きれば利用する.

例:
IOException, NoSuchFileException, IllegalArgumentException, などは 利用しやすい標準例外.
新たな例外の作成は,そのパッケージ全体のインターフェイスとして検討すること.

(56) メソッド引数の変更は悪

原則としてメソッドの引数は入力であり,出力としては使わないこと.
すなわちメソッド 内部で引数の状態を変更するメソッドを呼ばないこと.
出力引数に新たなオブジェクトを 代入しないこと(可能ならfinal とせよ).

悪い例:
void moveX(Point p, int dx) {
p.setX(p.getX()+dx); // 引数を変更している(なるべく避ける)
}
void moveX(Point p, int dx) { p = new Point(p.getX()+dx, p.getY()); // これは呼び出し側に伝わらない } 例外: パフォーマンスを気にする場合

(57) メソッド引数の名前

メソッドの引数は,読みやすいものにすること.
特に,インスタンス変数と重なった場合, this を活用し,引数の読みやすさを犠牲にしないこと.

悪い例:
void reset(int x_, int y_) {
x = x_;
y = y_;
}

良い例:
void reset(int x, int y) { // 引数名を x_, y_ などとしない
this.x = x;
this.y = y;
}

(58) toString()

toString() メソッドは可能ならいつでも実装すること.

理由 1: System.out.println(object) でいつでもプリントできる.
理由 2: ユニットテスト等で失敗した場合の表示が分かりやすくなる.

(59) switch,if/else の繰り返しは悪

switch 文で分岐する処理が現れた時には,よくない設計の兆候だと考え,ポリモーフィ ズムで実現できないか再考する.
特に同じようなswitch が2 箇所以上現れたら,必ずポ リモーフィズム,FactoryMethod,Prototype パターン等でリファクタリングすること.
if/else の連続も同様.
さらに,null チェックを行う同様のif が多くの場所に現れた ら,NullObject パターンの利用を検討せよ.

(60) String と基本型との変換

int から String またその逆変換は,以下のようにする(他の基本型も同様).

String s = String.valueOf(i);
int i = Integer.parseInt(s);

理由: 他の書き方もあるが,上記が最も分かりやすく効率的.

別法: (推奨しない)
String s = “” + i;
String s = new Integer(i).toString();
String s = Integer.toString(i); // これは悪くない
int i = new Integer(s).intValue();
int i = Integer.valueOf(s).intValue();

(61) コレクション

環境が許せば,JDK1.2 以降のコレクションクラスを用いよ.
すなわち,Vector, Hashtable, Enumeration で は な く , List (ArrayList), Map(HashMap), Iterator を好め.

理由 1: より簡潔で論理的,一貫性のあるメソッド名が使える.
理由 2: List, Set, Map インターフェイスにより,インターフェイスを変更せずに実装 を取り替えることが可能.
理由 3: 同期化がオプションであるため,より高速なコードが書ける(可能性がある).

参考: JDK1.2 コレクションについての利用ガイド
http://www.objectclub.jp/technicaldoc/java/jdk

©2014 Powered by 株式会社開成