関数型っぽいライブラリ作ってみました(Java)4 リフレクション | Hello, Stupid World!

Hello, Stupid World!

いろいろとメモ代わりに書いていきます。

以前から作っていたライブラリを使い勝手よくしようとずっと
いじってましたがやっと満足いくレベルになりました。
ライブラリというか、もはや簡易フレームワークですが。

まず中でArrayList固定だったのを指定したCollectionクラス(ListとかSetとか)
で保持できるよう修正しました。

これは最初、ジェネリクスで指定すれば良いかと考えてましたが
やってみるとできません。

ジェネリクスは型を指定できるだけであってインスタンス化するクラスを
指定できる訳ではないので

new T()とかできません。

次に考えたのは継承でした。
実際にnewする部分はサブクラスにしてそちらでnewすれば良いです。
これはうまくいきました。
ただ、使いたいCollectionクラスに必要になるし、継承しないといけないと
いうのは正直面倒でした。
委譲という手も同じ面倒さがあります。

そこでリフレクションはどうだろうかと調べました。
文字列をクラスを指定するClass#forNameは避けて
Class#newInstanceを使用しました。
こうして動的に使用する側が内部で保持するクラスを指定できるように
なりました。

以下、ライブラリのソースを解説します

「ソース&解説」

public static <T> Functional<T> set(Collection<T> source, Class<?> clz) throws Exception{
 @SuppressWarnings("unchecked")
 Collection<T> dest = (Collection<T>) clz.newInstance();
 if(source.isEmpty()){
  return new Functional<T>();
 }

 T elem = source.iterator().next();
 if(elem instanceof Copyable){
  for (T item : source) {
   dest.add(((Copyable) item).deepCopy(item));
  }
 }else{
  for (T item : source) {
   dest.add(item);
  }
 }
 Functional<T> functional = new Functional<T>(dest);
 functional.clz = clz;
 return functional;
}

修正したインスタンスを生成するstaticメソッドです。
引数として保持するデータを受取るとともにそのデータを入れるクラス型を
受取ります。

newInstanceでCollection型のインスタンスを生成し、データを追加していきます。
この時、データの要素の型が自作のCopynableインタフェースを実装している
クラスならばdeepCopyというメソッドを呼出すので
必要ならばdeepCopyメソッド内でディープコピーを返すようにして下さい。

Copynableインタフェースを実装していない場合は普通にaddで追加します。
不変クラスでない場合はシャローコピーとなってしまうので注意して下さい。

その後、自分のインスタンスを生成しデータを格納するクラスをメンバとして
セットし戻します。

他も同じように修正しています。

public Functional<T> edit(Editor editor) throws Exception{
 @SuppressWarnings("unchecked")
 Collection<T> list = (Collection<T>) clz.newInstance();
 for (T item : this.list) {
  list.add(editor.<T>edit(item));
 }
 Functional<T> functional = new Functional<T>(list);
 functional.clz = clz;
 return functional;
}

こちらも同様にsetで保持しておいたclz(Class)を使って
インスタンスを生成しています。

他メソッドも大体同じですね。
あと、今回は新しいメソッドとしてloadというものを用意しました。
これはsetと同じように使うのですが、setはデータを呼出し元から直接渡すのに
対してloadはLoadableインタフェースを実装したクラスからセットします。

固定値など簡単に渡せるものはset、DBやファイルから取得したものをデータと
する時はloadを使って下さい。

「使い方例」

//固定値をHashSetに入れて取得
Set<String> list = (Set<String>) Functional.<String>load(new FixLoad(), HashSet.class)
 .get();

//DBにあったら更新
Functional.<TestBean>set(arr).filter(new Exist()).exec(new Update());

//DBになかったら登録
Functional.<TestBean>set(arr).filter(new NotExist()).exec(new Insert());

//DBから読み込んで画面出力
Functional.<UserBean>load(new Select()).exec(new AllPrint("User = "));

上記のような形で使います。
filterやexec内で使用しているクラスは各自で用意する必要がありますが
このライブラリを使うことで上記のように繰返しや条件式を除外した
可読性の高いスッキリした形で記述できます。

それぞれ日本語で機能を説明したままのソースにできるので
分かりやすいと思います。
固定値(FixLoad)をHashSetに入れて取得(get)
DBにあったら(Exist)更新(Update)
など


JARファイルを作成したので置いておきます。
http://trapz.web.fc2.com/blog/lib/FunctionLibrary.jar