TOP設計・移行・活用> abstractメソッドを利用する(C_IHT002)
Javaコーディング規約
Javaコーディング規約

第9回:継承とインスタンスに関わるコーディング規約
著者: 電通国際情報サービス
高安 厚思、東田 健宏、岡 薫   2005/12/2
前のページ  1  2   3  4  次のページ
abstractメソッドを利用する(C_IHT002)

   抽象クラスは、インターフェースと同様にそれ単体ではインスタンス化できないクラスです。

   インターフェースとの違いは部分的に実装を提供可能であるという点です。実装を提供しないメソッドにはabstract修飾子を付与して抽象メソッドとすることで、サブクラスでの実装を強制させることができます。

   特殊なケース(注1)を除いて、実装が空であるメソッドを作成すべきではありません。空メソッドの実装をサブクラスでオーバーライドするという意図があるならば、抽象メソッドとすることで、その意図を明確にする必要があります。抽象メソッドとせずに空メソッドを定義していた場合、サブクラスでの実装を忘れる可能性があります。

※注1: 空メソッドを提供する典型的な例としては、NullObjectパターンがあります。詳細は、下記のURLなどを参照してください。
http://www.hyuki.com/dp/dpinfo.html#NullObject
スーパークラスでprivate宣言されているメソッドと同じ名前のメソッドをサブクラスで定義しない(C_IHT003)

   スーパークラスのインスタンスメソッドがprivateである場合、サブクラスで同一のシグニチャを持つインスタンスメソッドを定義することが可能です。この際、これらのメソッドに「オーバーライド」の関係はなく、各々のクラスに従属する独立したメソッドとして動作します。

   スーパークラスのインスタンスメソッドがprivateである場合、サブクラスで同一シグニチャを持つインスタンスメソッド、つまりは「同一名称のメソッド」を定義すべきではありません。これは、C_IHT001の規約で説明した「継承によるフィールドの隠蔽」と同様にサブクラスの可読性を低下させる原因となります。

   一方、スーパークラスのインスタンスメソッドの可視性をprivateに変更する際には、細心の注意を払う必要があります。当該メソッドがサブクラスによってオーバーライドされている場合、この変更にともないオーバーライドの関係が絶たれることになります。場合によっては、コンパイルエラーが発生しないこともあるため、変更者が意図していなかった箇所で影響がでるといった危険性があります。


equals()メソッドを実装した場合は、コレクションクラスに格納することを想定してhashCode()メソッドも実装する(C_IHT004)

   ハッシュテーブルは、「一対のキーと値」として表現される要素を格納するデータ構造です。この構造に基づいたコレクションクラス(HashMap、HashSet、Hashtableなど)を利用する際、これらのコレクションクラスの動作を保証するためには、キーとなるクラスのequals()メソッドおよびhashCode()メソッドが正しく定義されている必要があります

   従ってequals()メソッドを実装した際には、こうしたコレクションクラスでの利用を想定し、hashCode()メソッドを実装するべきです。本規約に違反した場合、コレクションクラスの動作が保証されずバグとなる危険性があります。

   それでは、Java2以降のコレクションクラスであるjava.util.HashMapを利用したサンプルプログラムを見てみましょう。

   下記サンプルでは、コレクションのキーとなるクラスIDNumberが定義されています。IDNumberにはequals()メソッドのみが再定義(オーバーライド)され、hashCode()メソッドは定義されていません。そのためObjectクラスのhashCode()メソッドが継承されます。

HashMapを利用したサンプルプログラム
public final class IDNumber {
     private final int id;
     public IDNumber(int id) {
          this.id = id;
     }
     public boolean equals(Object object) {
          if (object == this) {
               return true;
          } else if (object instanceof IDNumber && this.id == ((IDNumber)object).id) {
               return true;
          }
          return false;
     }
     public static void main(String[] args) {
          Map map = new HashMap();
          Object initialKey = new IDNumber(123);
          map.put(initialKey, "Oka");
          Object findKey = new IDNumber(123);
          // (1) "true"が表示される
          System.out.println(findKey.equals(initialKey));
          // (2) "null"が表示される
          System.out.println(map.get(findKey));
     }
}
   equals()メソッドは、HashMapコレクションに値を格納する際に利用したキー(initialKey)と取得に利用したキー(findKey)が等しい(1)としているにも関わらず、HashMapコレクションからは値"Oka"は返却されず、"null"が返されました(2)。

   先ほど、ハッシュテーブルに基づくコレクションの動作が保証されるには、「キーとなるクラスのhashCode()メソッドとequals()メソッドが正しく定義されている」必要があると説明しましたが、これらのメソッドが正しく定義されているとは、hashCode()がequals()の結果に対して、次の条件を満たす整数値を返すことを意味します。

  1. equals()メソッドで利用されたフィールドに変更がない限り、同一のハッシュ値を返す
  2. equals()メソッドでオブジェクトが等しいと判断された場合、ハッシュ値も同様に等しくなる

表1:メソッドの正しく定義

   Objectクラスのequals()メソッドは、nativeメソッドでその実装はJava VMに依存しますが、異なるオブジェクト参照を持つinitialKeyとfindKeyは異なる整数値を返します。このため、表1の2が満たされずに値"Oka"が取得されなかったことになります。

   表1の1と2を満たすhashCode()メソッドの最も簡単な実装は、IDNumberクラスにおいて"return 0;"であるhashCode()メソッドを再定義することです。これにより、値"Oka"が取得できるようになります(注2)。

※注2: この実装はパフォーマンス面に問題があります。パフォーマンスを考慮した実装を書くには高度な知識が必要となります。この問題に対するひとつの解は、Jakarta Commonsプロジェクトで提供されているLangコンポーネントを利用することです。

Langコンポーネントの利用方法は、下記の書籍などを参考にしてください。
「Jakarta Commonsクックブック -Javaプロジェクト必須のレシピ集」
前のページ  1  2   3  4  次のページ


株式会社電通国際情報サービス 開発技術センター 高安 厚思
著者プロフィール
株式会社電通国際情報サービス  高安 厚思
株式会社電通国際情報サービス 開発技術センター
Java(J2EE)/オブジェクト指向の研究開発やプロジェクト支援、開発コンサルティングに従事。モデル、アーキテクチャ、プロセスが探求対象。今回は Javaコーディング規約2004の仕掛け人。


株式会社電通国際情報サービス 開発技術センター 東田 健宏
著者プロフィール
株式会社電通国際情報サービス  東田 健宏
株式会社電通国際情報サービス 開発技術センター
CTI、Webアプリの開発経験を経て、現在は主にプロジェクトマネジメント支援、プロセスエンジニアリング、ソフトウェア工学研究開発に従事。最近はコーチング、ファシリテーションといったヒューマン系スキルに興味を持ち日々修得に努めている。


株式会社電通国際情報サービス 岡 薫
著者プロフィール
株式会社電通国際情報サービス  岡 薫
株式会社電通国際情報サービス アウトソーシング事業部所属
Java(J2EE)によるフレームワーク開発やWebのセキュリティ研究開発の経験を経て、現在は、大手企業の管理会計システムの開発プロジェクトに従事。多次元データベースの検索パフォーマンスを向上することに日々頭を悩ませている。


INDEX
第9回:継承とインスタンスに関わるコーディング規約
  はじめに
abstractメソッドを利用する(C_IHT002)
  可能な限りtoString()メソッドを実装する(C_IHT005)
  Class名を利用した比較はおこなわない(C_IST002)