japan.internet.com The Internet & IT Network


RSSニュース検索
カテゴリ
> トップページ
> Webビジネス
> Eコマース
> Webファイナンス
> Webマーケティング
> パブリック
> Webテクノロジー
> 携帯・ワイヤレス
> Linux Today
> Linux Tutorial
> J.I.C.ブログ
キャリア
> 転職ならen
> 派遣ならen
> アルバイトならen
> IT求人情報
ヘッドライン
> 今日のヘッドライン
> 週間ヘッドライン
Special Link
> フォトコミュニティ
> ストックフォト
> クリップアート
> イラスト
> フェリカ
> Web2.0
> 写真
イベント&セミナー
> イベントカレンダー
> 書評「IT の耳」
> 出張・接待検索
> ニュースガジェット 注目
無料ニュースメール
> 新規登録
> 変更・解除
> オプトインメールの登録・変更・解除
インフォメーション
> パートナーサイト
転職ならエン
就職ならen
求人ならen
履歴書ならen
アルバイトならエン
CRM/SFAならオラクル
> グループ会社
株式会社アエリア
(株)サンゼロミニッツ
株式会社エアネット
> お問い合わせ
> 広告掲載について
> リンクについて
> 著作権について
> その他お問い合わせ
> 利用規約
> 個人情報保護方針
> 会社概要地図
デベロッパー 2008年5月2日 10:00
デベロッパー・バックナンバー
Javaジェネリックを使ったコンパイル時の動的処理

著者: Sumith Puri  オリジナル版を読む プリンター用 記事を転送
2008年5月2日 10:00 付の記事
海外internet.com発の記事
このエントリーを含むはてなブックマーク この記事をクリップ! Buzzurlにブックマーク Yahoo!ブックマークに登録 newsing it!

はじめに

 本稿では、ジェネリックを利用してコンパイル時の動的処理およびクライアント関連の型安全性(type-safety)を実現する方法について解説します。一般的に、サブクラス化を行う際の最も重要な側面は、クラス固有の機能を実現するために、いかにして同じメソッドパラメータを使ってオーバーライドを実現するかということです。場合によっては、クラス固有のパラメータが必要になることもあるかもしれません。さらに、オーバーライドメソッドが、こうしたクラス固有パラメータのスーパークラスであるパラメータを使用する場合も考えられます。このようなメソッドの例としては、パブリックAPIを通じて公開され、具象実装クラス内でオーバーライドされるメソッドが挙げられます。

シナリオ

 本稿で取り上げるのは非常によくあるシナリオで、多くの人が過去に直面したことがあり、同じような方法で解決してきたのではないかと思います。ここでは、単純なレンタカーシステムの例を使ってジェネリックの利用方法を説明します。

図1 オブジェクトのリレーション図
図1 オブジェクトのリレーション図
 今回の例では、メインのレンタルサービスクラスをRentVehicleManagerとしますが、このクラスはインターフェイスにしても抽象クラスにしてもかまいません。RentCarManagerとRentBikeManagerはRentVehicleManagerのサブクラスで、RentVehicleManagerのメソッドをそれぞれの機能に合わせてオーバーライドします。このシナリオの実用的な実装ソリューションとしては、次のようなものが考えられます。

  1. 一連のManagerクラスの作成にFactoryパターンを使用する
  2. パラメータの共通階層を作り、メソッド内で厳密なチェックを行う
  3. ジェネリックを利用した共通階層を使用する
 方法1は、関連クラスのインスタンス生成という点では優れたソリューションですが、オブジェクトパラメータの一般化は実現できません。

 方法2のソリューションは、たとえば図2のようになります。この場合、個々のオーバーライドメソッドは、そのクラスにふさわしい型の値が引数に渡されているかどうかをメソッドの冒頭で厳密にチェックしなければなりません。

図2 クラス図(方法2の場合)
図2 クラス図(方法2の場合)
 この場合のインターフェイスは、たとえば次のようになります。

package com.sumithp.codeguru.nongeneric.vehicle;

import com.sumithp.codeguru.vehicle.domain.Vehicle;

public interface RentVehicleMgr {

   public void rentOut(Vehicle vehicle);
   public void checkIn(Vehicle vehicle);
   public void diagnose(Vehicle vehicle);
   public void repair(Vehicle vehicle);
}
 方法2の場合、バイクレンタルに関する実装はたとえば次のようになります。

package com.sumithp.codeguru.nongeneric.vehicle;

import com.sumithp.codeguru.vehicle.domain.Vehicle;

public class RentBikeMgrImpl implements RentVehicleMgr {

   // If we don't use Vehicle as the parameter here, the clients
   // will not be able to use a generalized interface to call our
   // methods.

   public void rentOut(Vehicle vehicle) {
      // if (vehicle instanceof bike)
         // Renting Out Related DB Operations
   }

   public void checkIn(Vehicle vehicle) {
      // if (vehicle instanceof bike)
         // Vehicle Check In Related DB Operations
   }

   public void diagnose(Vehicle vehicle) {
      // if (vehicle instanceof bike)
         // Self Diagnose functionality of a vehicle
         // Print diagnosis
   }

   public void repair(Vehicle vehicle) {
      // if (vehicle instanceof bike)
         // Perform pre-defined repair
         // Print repair details
   }
}
 方法2の場合、コンパイル時のクライアント側の使い方は次のようになるでしょう。

package com.sumithp.codeguru.nongeneric.vehicle.client;

import com.sumithp.codeguru.nongeneric.vehicle.RentBikeMgrImpl;
import com.sumithp.codeguru.nongeneric.vehicle.RentCarMgrImpl;
import com.sumithp.codeguru.nongeneric.vehicle.RentVehicleMgr;
import com.sumithp.codeguru.vehicle.domain.Bike;
import com.sumithp.codeguru.vehicle.domain.Car;
import com.sumithp.codeguru.vehicle.domain.Vehicle;

public class RentNonGenericVehicleClient {

   public void rentBike() {

      // You want only one interface to handle all rentals
      RentVehicleMgr rentVehicleMgr;

      rentVehicleMgr = new RentBikeMgrImpl();

      Vehicle vehicle = new Bike(104,"TWO",true,150);
      rentVehicleMgr.rentOut(vehicle);

      /*
       * Client can as well do this
       *
       * Vehicle vehicle = new Car(104,"FOUR",true,"PETROL");
       * rentVehicleMgr.rentOut(vehicle);
       *
       * If there are no instanceof checks, this bombs!
       *
       */
   }

   public void rentCar() {

      // You want only one interface to handle all rentals
      RentVehicleMgr rentVehicleMgr;

      rentVehicleMgr = new RentCarMgrImpl();

      Vehicle vehicle = new Car(104,"FOUR",true,"PETROL");
      rentVehicleMgr.rentOut(vehicle);

      /*
       * Client can do the same as shown for rentBike()
       *
       * Vehicle vehicle = new Bike(104,"TWO",true,150);
       * rentVehicleMgr.rentOut(vehicle);
       *
       * If there are no instanceof checks, this bombs too!
       *
       */
   }

}
 方法3は、最も完成度が高く、有効なソリューションです。必要なメソッド群は1つのクラスで公開され、各メソッドはジェネリック変数を通じてそれぞれの実装を提供するため、一貫性に優れています。それでは、このソリューションについて詳しく見ていきましょう。

Javaジェネリックを使った共通階層

 ジェネリックの大きな長所は、その「型消去(Type Erasure)」という性質です。これはつまり、コンパイル時チェックのみが行われ、その後はジェネリック変数が消去され、実行時の検証は行われないということを意味します。一方、これには短所もあり、サードパーティ製のコードと併用するときに安全性が保証されないなどの問題があります(内部コードでも、ジェネリックを使用しないコードであれば同様の問題が起こります)。

 このソリューションを実現するには、方法2に簡単な変更を加え、階層内の各クラスに新しいジェネリック変数を追加します。トップレベルでは、RentVehicleManagerの宣言を、Vehicleを拡張(extends)する型のジェネリック変数を使用するように書き換えます。次に例を示します。

package com.sumithp.codeguru.generic.vehicle;

import com.sumithp.codeguru.vehicle.domain.Vehicle;

public interface RentVehicleMgr< T extends Vehicle > {

   public void rentOut(T vehicle);
   public void checkIn(T vehicle);
   public void diagnose(T vehicle);
   public void repair(T vehicle);
}
 さらに、RentVehicleManagerを継承するクラス(つまりRentBikeManagerとRentCarManager)の宣言を、それぞれにふさわしい型のジェネリック変数を使用するように書き換えます。たとえばカーレンタルに関する実装は次のようになります。

package com.sumithp.codeguru.generic.vehicle;

import com.sumithp.codeguru.vehicle.domain.Car;

public class RentCarMgrImpl implements RentVehicleMgr< Car > {

   // Can use Car as parameter here, as well as allow
   // clients to have a generalized interface

   public void rentOut(Car car) {
      // Renting Out Related DB Operations
   }

   public void checkIn(Car car) {
      // Vehicle Check In Related DB Operations
   }

   public void diagnose(Car car) {
      // Self Diagnose functionality of a vehicle
      // Print diagnosis
   }

   public void repair(Car car) {
      // Perform pre-defined repair
      // Print repair details
   }
}
 これはつまり、クライアント側から特定の種類のManagerクラスのメソッドを呼び出すときは、目的クラスに対応する型のオブジェクトを渡さなければならないということです。これにより、Manager実装クラスはメソッド内で厳密なチェックを行わずに済むようになります。また、こうしたサブクラスのメソッドを修正するときに、instanceofチェックを含める必要はなくなります。

図3 クラス図(方法3の場合)
図3 クラス図(方法3の場合)
 これでクライアントコードは一層すっきりし、安全になります。一般的な実装は次のようになります。

package com.sumithp.codeguru.generic.vehicle.client;

import com.sumithp.codeguru.generic.vehicle.RentBikeMgrImpl;
import com.sumithp.codeguru.generic.vehicle.RentCarMgrImpl;
import com.sumithp.codeguru.generic.vehicle.RentVehicleMgr;
import com.sumithp.codeguru.vehicle.domain.Bike;
import com.sumithp.codeguru.vehicle.domain.Car;

public class RentGenericVehicleClient {

   public void rentBike() {

      // You want only one interface to handle all rentals
      RentVehicleMgr< Bike > rentVehicleMgr;

      rentVehicleMgr = new RentBikeMgrImpl();

      Bike bike = new Bike(104,"TWO",true,150);
      rentVehicleMgr.rentOut(bike);

      /*
       * Client cannot do this:
       *
       * Vehicle vehicle = new Car(104,"FOUR",true,"PETROL");
       * rentVehicleMgr.rentOut(vehicle);
       *
       * Even if there are no instanceof checks, all is well!
       * Client is absolutely clear on what he needs to do.
       *
       */
   }

   public void rentCar() {

      // You want only one interface to handle all rentals
      RentVehicleMgr< Car > rentVehicleMgr;

      rentVehicleMgr = new RentCarMgrImpl();

      Car car = new Car(104,"FOUR",true,"PETROL");
      rentVehicleMgr.rentOut(car);

      /*
       * Client cannot do the following as shown for rentBike():
       *
       * Vehicle vehicle = new Bike(104,"TWO",true,150);
       * rentVehicleMgr.rentOut(vehicle);
       *
       * Even if there are no instanceof checks, all is well!
       * Client is absolutely clear on what he needs to do.
       *
       */
   }
}
 このような設計にすると、保守やコーディングがしやすくなるだけでなく、実装についての理解もしやすくなります。この設計方法は、サービスや機能の実装側クラスだけでなく、サービスのコンシューマ側クラスにとってもメリットがあります。

まとめ

 本稿で紹介したジェネリックの使い方は動的継承に役立ちますが、これはコンパイル時の動的処理を実現するだけです。前にも述べたとおり、実行時にはジェネリック変数はクラスから削除されます。

著者紹介

Sumith Puri(Sumith Puri)
4年以上にわたりJava/J2EEテクノロジを使ったアプリケーションの設計/開発に携わる。現在はSymantec India(インド、プネー)の開発者として勤務。SRSIT(インド、バンガロール)にて情報工学の学士号を取得。Sun Certified Java Programmerの資格を持つ。


関連記事
  • Sony Ericsson、『Java ME』と『Flash Lite』の連携技術を発表
  • 日本 SCO、スマートフォンから Windows/UNIX システムを管理するツールを
  • XMLデータの変更をSDOで簡単に追跡する
  • CNN と SAMSUNG 電子、モバイルニュースアプリの独占搭載契約を発表
  • 日立ソフト、修正箇所の問題点のみを抽出する Java コード診断ツールを販売


  • 関連テーマ
  • Sun
  • Java


  • ★最新トップニュース
    国内 SAP ジャパン、中堅企業向け新 ERP 導入プログラムを提供開始(Webビジネス 7月23日 18:00)
    SAP ジャパンは2008年7月23日、2008年8月より、中堅企業向け ERP の短期間・低コスト導入プログラム「SAP Business All-in-One FAST-START PROGRAM(エスエイピービジネスオールインワンファーストスタートプログラム)」を開始すると発表した。
    海外 【インド】RCOM、MTN との合併交渉成立せず(Webファイナンス 7月23日 18:00)
    アニル・アンバニ氏率いる ADA グループの旗艦企業、Reliance Communications(RCOM)と南アフリカ通信最大手の MTN とのほぼ2か月に及んだ合併交渉は18日、白紙撤回という形で結末を迎えた。RCOM が18日深夜に発表したところによれば、両社は独占協定を撤回することで相互同意したという。
    海外 【中国】Google、青島市などで「Adsense」のプロモーションを展開(Webマーケティング 7月23日 18:00)
    7月22日―中国国内メディアの報道によれば、Google は7月25日から7月30日にかけて、青島市・合肥市・南寧市で Google の広告サービス、「Adsense」のプロモーションを展開する予定を明らかにしたという。
    国内 OKI データ、タイ工場で従業員に省エネ型蛍光ランプを配布(Webビジネス 7月23日 17:50)
    今回無償交換した電球型蛍光灯は、形は白熱灯と同じだが、白熱灯のソケットにそのまま取り付けられるもので、電力消費量が白熱灯の約4分の1、寿命は白熱灯の約6倍。ただし、価格は白熱灯の8〜10倍であることが普及のネックとなっている。
    国内 プリミアイメージ、ソフトウェアの対米輸出サポートサービスを開始(Webマーケティング 7月23日 17:30)
    株式会社プリミアイメージは、2008年7月、アメリカでの「法人向けソフトウェア輸出・ソフト卸仲介サービス」を開始すると発表した。
    トピックス
    > オススメのIT系求人情報【毎週月曜日更新】
    footer_301.gif


    リサーチ
    > デイリーリサーチDLサイト
    > OnlineResearchPortal (リサーチデータバンク)
    > モバイルリサーチ with goo
    footer_301.gif
    キーワード
    > iPhone > Youtube
    > Google > モバイルノート
    > 半導体 > ウィルコム
    > テーマ一覧はこちら
    footer_301.gif
    セミナー情報
    > 第1回インターネットコムマーケティングセミナー「新規クライアントを効率的に獲得する Web マーケティング手法とは」(3月26日)多数のご参加ありがとうございました
    footer_301.gif
    デベロッパー
    > DevX
    > CodeGuru
    > developer.com
    footer_301.gif
    日本Oracle
    footer_301.gif
    j.i.c.ブログ
    ブログ一覧
    データメーション 【データメーション】
    サンフランシスコの悪者エンジニアは過労が怒りにつながったとする情報筋(7月23日)
    Graphic Design Forum 【Graphic Design Forum】
    グラフィカル系アフィリエイトプログラム (7月23日)
    エンジニアの独り言 【エンジニアの独り言】
    新入社員が配属される季節ですね。(7月23日)
    ジュピターメディア創設者がITを斬る 【ジュピターメディア創設者がITを斬る】
    Alan を探せ(7月18日)
    ベンチャー専門家の目利きブログ「なぜこの企業は伸びるのか?」 【ベンチャー専門家の目利きブログ「なぜこの企業は伸びるのか?」】
    「『訪問歯科診療』のパイオニア」/デンタルサポート株式会社(7月15日)
    デスマーチからの脱却 【デスマーチからの脱却】
    mod_rewriteを使ったiPhone対応(7月12日)
    footer_301.gif
    最新コラム一覧
    Eメールマーケティング事情 Eメールマーケティング事情

    大量送信のスパムからターゲット絞り込みスパムメールへ(7月23日)
    日本と韓国のインターネットビジネス最新動向調査 日本と韓国のインターネットビジネス最新動向調査

    日本と韓国の Eコマースサイト比較1―EC サイト利用方法(7月23日)
    百式のネットビジネス研究 百式のネットビジネス研究

    2枚の写真から子供の合成写真を作ってくれる「Make Me Babies」(7月23日)
    DevX DevX

    PHP、MySQL、JavaScriptによるセキュアなインターネットファイル共有システムの作成(7月22日)
    モバイルSEO@フラクタリスト モバイルSEO@フラクタリスト

    Yahoo! モバイルの特徴とモバイル SEO の初期対策(7月22日)
    エンジニア転職ノウハウ開発室 エンジニア転職ノウハウ開発室

    遠距離恋愛支援システムを作ったユビキタスの椎尾一郎(7月22日)
    アイレップの SEM フロンティア アイレップの SEM フロンティア

    Google ユニバーサル検索「地図検索」の重要性(7月22日)
    最新ハイテク講座 最新ハイテク講座

    10万馬力どころじゃない! 小型ノート PC を超安くした「Atom」(7月18日)
    アイレップの SEO スタンダード アイレップの SEO スタンダード

    PC SEO 対策とモバイル SEO 対策5つの相違点(7月18日)
    「IT の耳」 「IT の耳」

    【書評】『グリーン IT』――コスト削減と温暖化対策を両立する IT 効率化の戦略(7月17日)
    footer_301.gif
    専門チャンネル
    > セキュリティチャネル > テレコムチャネル
    > サーチエンジンウォッチ
    footer_301.gif
    海外のインターネットコム アメリカ韓国ドイツトルコ
    関連企業のサイト:ストックフォト イラスト ネットストリート ホテル予約サイト タウン情報 出張 事業継承 シミュレーション トランクルーム 優待映画チケット 田舎暮らしガイド オリジナルデザインTシャツ ニタコエ
    Copyright 2008 Jupitermedia Corporation All Rights Reserved. http://www.internet.com/
    space.gif space.gif