JavaのGenericsはこんなに便利だったのか!

今までListに対する型指定ぐらいしか使ってなかったGeneric。
Genericを使ったクラスを作ってるのは業務でも見る事がなかったけど、
先日から現場でいっしょになった方にいろいろ教えて貰った。
そもそもJava5を使う機会があんまりなかったのもあるけど。

Javaやってて違和感があるのが、Objectを引数にするときと、
キャストをするとき。せっかく指定した型が台無しになる。
型を指定する言語である以上、指定した型は大事にしたいというか。

型を大事にしつつ、処理の大まかな流れが流れが似通ったクラスをまとめる場合は、
Genericsが大変便利な事を思い知った。
例えば業務アプリではよくあるこんな流れ
1.DataBeanにDBから値を詰め込む
2.データチェック
3.画面に表示

こんな場合にInterfaceがなくてもGenericesは効果を発揮する。
下の例ではDBから値をとってくる所を、お手軽にメソッドで代用してみた。
public class Template {
    public static void main (String[] args) {
	DisplayFooBean dispFoo = new DisplayFooBean();
	dispFoo.dispData();
	DisplayHogeBean dispHoge = new DisplayHogeBean();
	dispHoge.dispData();
    }
}

class DisplayFooBean {
    public void dispData() {
	FooBean fooBean = getData();
	if (fooBean != null) {
	    System.out.println(fooBean.getFoo());
	}
    }

    private FooBean getData() {
	FooBean fooBean = new FooBean();
	fooBean.setFoo("foo");
	return fooBean;
    }
}

class DisplayHogeBean {
    public void dispData() {
	HogeBean hogeBean = getData();

	if (hogeBean != null) {
	    System.out.println(hogeBean.getHoge());
	}
    }

    private HogeBean getData() {
	HogeBean hogeBean = new HogeBean();
	hogeBean.setHoge("hoge");
	return hogeBean;
    }
}

class FooBean {
    private String foo;

    public final String getFoo() {
	return this.foo;
    }

    public final void setFoo(final String newFoo) {
	foo = newFoo;
    }
}

class HogeBean {
    private String hoge;

    public final String getHoge() {
	return hoge;
    }

    public final void setHoge(final String newHoge) {
	this.hoge = newHoge;
    }
}
Genericsを使うと、

public class Template {
    public static void main (String[] args) {
	DisplayFooBean dispFoo = new DisplayFooBean();
	dispFoo.dispData();
	DisplayHogeBean dispHoge = new DisplayHogeBean();
	dispHoge.dispData();
    }
}

abstract class AbstractDisplay <T> {
    public void dispData() {
	T t = getData();
	if (t != null) {
	    display(t);
	}
    }
    abstract void display(T t);
    abstract T getData();
}

class DisplayFooBean extends AbstractDisplay <FooBean> {
     void display(FooBean fooBean) {
	System.out.println(fooBean.getFoo());
    }
    FooBean getData() {
	FooBean fooBean = new FooBean();
	fooBean.setFoo("foo");
	return fooBean;
    }
}

class DisplayHogeBean extends AbstractDisplay <HogeBean> {
    void display(HogeBean hogeBean) {
	System.out.println(hogeBean.getHoge());
    }

    HogeBean getData() {
	HogeBean hogeBean = new HogeBean();
	hogeBean.setHoge("hoge");
	return hogeBean;
    }
}

class FooBean {
    private String foo;

    public final String getFoo() {
	return this.foo;
    }

    public final void setFoo(final String newFoo) {
	foo = newFoo;
    }
}

class HogeBean {
    private String hoge;

    public final String getHoge() {
	return hoge;
    }

    public final void setHoge(final String newHoge) {
	this.hoge = newHoge;
    }
}
AbstractDisplayクラスでは継承先に依存するBeanの部分をパラメータにしておき、
処理の流れを記述する。
AbstractDisplayクラスを見れば、処理の流れは大体分かるし、
継承先のDisplayHogeクラスではextends AbstractDisplay <HogeBean>と、型を指定してるので、
キャストも発生しない。

こんな感じで
  • InterFaceがなくても共通化がスマートにできて、
  • キャストも発生しない

C++使いにとっては、まだまだ物足りないJavaのGenericらしいけど、
もうちょっとはやってもいいよなぁ。


Comment

  1. Saxman
    2007-11-12 Mon 07:08

    私もまだJavaのGenericはあまり知りませんが、どうも型の安全性を確保するために作られたような気がします。
    今までのListなどのコレクションではObject型だったため、実質なんでも入れれたけど、そこに制約を設け、例えば「Serializableなオブジェクトしか登録できないようにする」みたいなのもできるので、使いようによっては安全性を維持したライブラリが簡単にできると思います。

    ただ、渡した型パラメータを使ってインスタンスを生成できない。instanceofなどの型情報を扱うものを使えない。など欲しい所で役立たずぅ!って思ってしまう。T.class.getName()みたいなことができればReflect使ってもっと便利に使えそうなのに・・・。

    でも、もうちょっとはやってもいいよなぁ(笑

  2. tma
    2007-11-13 Tue 22:28

    T.class.getName() は自分も使えなくてはがゆい思いをしました。
    こういう所がはやらない理由なんですかねぇ。