CodeZine(コードジン)

特集ページ一覧

Javaジェネリックを使ったコンパイル時の動的処理

ジェネリックを利用した型安全性の実現

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加
2008/03/13 14:00

ダウンロード サンプルソース (6.9 KB)

本稿では、ジェネリックを利用してコンパイル時の動的処理およびクライアント関連の型安全性(type-safety)を実現する方法について解説します。

目次

はじめに

 本稿では、ジェネリックを利用してコンパイル時の動的処理およびクライアント関連の型安全性(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つのクラスで公開され、各メソッドはジェネリック変数を通じてそれぞれの実装を提供するため、一貫性に優れています。それでは、このソリューションについて詳しく見ていきましょう。


  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加

あなたにオススメ

著者プロフィール

  • Sumith Puri(Sumith Puri)

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

  • japan.internet.com(ジャパンインターネットコム)

    japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.com や EarthWeb.c...

バックナンバー

連載:japan.internet.com翻訳記事

もっと読む

All contents copyright © 2005-2021 Shoeisha Co., Ltd. All rights reserved. ver.1.5