The HIRO Says

If you smell what The HIRO is cooking!!!

【generics】generic singleton factory

以前、generics では実行時に型情報がなくなることを紹介しました。
これを singleton とうまく合わせると、任意の型を扱える singleton を定義できます。


手順

  1. generic なインターフェースを作成する。
  2. generic なインターフェースを返すファクトリクラスを作成する。

generic な singleton を factory で提供することから、そのまま”generic singleton factory”というパターン名がついています。
(Effective Java 2nd edition - P131)

以下、具体例を示します。


(1)generic なインターフェース

ひとまず、こんなインターフェースを定義するとして下さい。

public interface Viewer {

    /**
     * 引数を表示する。
     * 
     * @param t 表示する値
     */
    void view(T t);
}

(2)ファクトリクラス

public class ViewerFactory {

    /**
     * Viewer の singleton。
     * 
     * 任意の型を扱えるようにするため、
     * type parameter に Object を指定。
     * 他は、通常の singleton と同じ。
     */
    private static final Viewer VIEWER =
        new Viewer() {
            public void view(Object obj) {
                System.out.println(obj);
            }
        };

    /**
     * Viewer の singleton を返す。
     * 
     * 任意の type parameter を指定できるように定義。
     * 他は、通常の singleton と同じ。
     * 
     * ちなみに、Viewer が immutable かつ stateless であること、
     * および任意の型を取り扱えることから、
     * 型の安全性は保障できる。
     * そのため、キャスト時の警告は抑制して問題ない。
     */
    @SuppressWarnings("unchecked")
    public static  Viewer getViewer() {
        return (Viewer) VIEWER;
    }
}


(3)検証用のテストコード

    public void testViewerFactory() {

        // type inference なので、
        // 左辺のみ type parameter を指定すればよい。
        Viewer  stringViewer = ViewerFactory.getViewer();
        Viewer intViewer    = ViewerFactory.getViewer();

        // singletonであることの確認
        assertSame(
                stringViewer,
                intViewer);

        // Viewer 自体の動作確認
        String stringArray = new String {"are", "kore", "sore"};
        int    intArray    = new int    {12,     34,    56};

        // キャストが不要!
        for (String str : stringArray) {
            stringViewer.view(str);
        }

        for (int i : intArray) {
            intViewer.view(i);
        }
    }


実行結果は、以下のようになります。
are
kore
sore
12
34
56