読者です 読者をやめる 読者になる 読者になる

No Programming, No Life

新しいNPNLです。http://d.hatena.ne.jp/fumokmm/ から引っ越してきました。

Javaで型トークンを利用したファクトリクラスを書いてみた

Java 小ネタ

はじめに

Javaインスタンスを生成するクラス、いわゆるファクトリクラスを書く場合、色々な書き方があると思いますが、特定のクラスのインスタンスのみしか作れないとわりと不便なので、今回は、型トークン(クラスクラスと呼ばれるやつですね: Class<T> です)を引数として渡してそのクラスのインスタンスを返却してくれるとファクトリを書いてみました。
さらにそのファクトリを通して一度生成しているインスタンスについては同じものを返してくれるとメモリの節約にもなってよいですね。

というわけで、早速ソースとテストコードです

説明

とりあえず、クラスがないと説明しにくいのでテスト用に以下のような階層のクラスを作ってあります。

Alphabet  <-- 抽象クラス
 |- A     <-- 具象クラスその1
 |- B     <-- 具象クラスその2
 +- C     <-- 具象クラスその3  

さて、ここで、Aのインスタンスを取得するためのファクトリの使い方は以下のようになります。

// Alphabetのサブクラス用のファクトリ
Factory<Alphabet> factory = new Factory<Alphabet>();
// Aのインスタンスを取得
A a = factory.getOrCreate(A.class);

#getOrCreateメソッドに型トークン(Class<A>)を渡すことにより、Aのインスタンスを取得できています。ファクトリ内部では、型トークンをキーとしたマップにインスタンスが保持されているため、再度 #getOrCreate(A.class) を呼び出した場合は同じインスタンスが取得されます。

A a1 = factory.getOrCreate(A.class);
A a2 = factory.getOrCreate(A.class);
a1 == a2; // => true: 同じインスタンス

もちろん、型トークンを変えれば違う型のインスタンスも取得できます。

B b = factory.getOrCreate(B.class);
C c = factory.getOrCreate(C.class);

ただし、Factory<T>はジェネリックなクラスとし、Tという型パラメータで指定されたクラスのサブクラスのみ生成可能となるよう制限をかけています。
#getOrCreateメソッドもジェネリックになっており、型パラメータUは<U extends T>としてありますので、Tのサブクラスの型トークンのみを引数として取れる、という風になっています。

public <U extends T> U getOrCreate(Class<U> clazz) { ... }

蛇足

昔、Java1.4の頃、これと同様のことをやりたくて、ファクトリクラスには生成する必要があるクラス分だけ、getterメソッドを作った記憶があります…今となってはいい思い出です。

class Factory14 {
  Foo createFoo() { ... }
  Bar createBar() { ... }
  Baz createBaz() { ... }
       .
       .
       .
}

おわりに

Javaジェネリックは理解して使うとコードもすっきりと整理できるし、そのクラスを利用する側も負担が減り、みんなが幸せになれる素晴らしいモノですね!これからも活用してゆきたいと思います。

参考書籍

Effective Java 第2版 (The Java Series)

Effective Java 第2版 (The Java Series)


主に第5章:ジェネリックスと項目29を参照