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

2007–11–10

今まで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らしいけど、 もうちょっとはやってもいいよなぁ。