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

ジェネリクスとか、enumとか

Java

特に意味は無いけど、ジェネリクスとかenumを使ってみよう。


// 関数のインターフェイス
public interface Function {
Y apply(X x);
}

まぁ、これはどこにでもあるようなインターフェイス。これを使って、ループを抽象化*1してみる。

何をやるかというと、ループの最初と最後の処理は普通の処理とは違う処理を施したいことが多い*2ので、それを支援できるようにしている。


public class Loop {

private Function normalProc; // 通常の処理
private Function firstProc; // 最初の処理
private Function lastProc; // 最後の処理

// 一個しか要素が無い場合の動作(デフォルトではlastProcを実行)
private ActionType actType = ActionType.LAST;

public static enum ActionType {
FIRST {
Y apply(Loop loop, X x) {
return loop.firstProc.apply(x);
}
}, NORMAL {
Y apply(Loop loop, X x) {
return loop.normalProc.apply(x);
}
}, LAST {
Y apply(Loop loop, X x) {
return loop.lastProc.apply(x);
}
};
// 関数を適用する(ジェネリックメソッド)
abstract Y apply(Loop loop, X x);
}

public Loop(Function normalProc) {
this.normalProc = normalProc;
this.firstProc = normalProc; // firstProcにも
this.lastProc = normalProc; // lastProcにもnormalProcを設定
}

// firstProcを設定しなおす
public void setFirstProc(Function firstProc) {
this.firstProc = firstProc;
}

// lastProcを設定しなおす
public void setLastProc(Function lastProc) {
this.lastProc = lastProc;
}

// ループが一回しか回らない場合の動作を設定する
public void setActionType(ActionType actionType) {
this.actType = actionType;
}

// argsに対して何か処理を施し、結果を返す
public List loop(List args) {
if (args == null || args.size() == 0)
return new ArrayList(0);

// 結果のリスト
List results = new ArrayList(args.size());

// 要素が一個だった場合、actTypeによって処理を振り分ける
if (args.size() == 1) {
results.add(actType.apply(this, args.get(0)));
return results;
}

// 2個以上ならイテレータを使用
Iterator itr = args.iterator();
// このチェックは冗長すぎるかも・・・
if (itr.hasNext())
results.add(firstProc.apply(itr.next()));
while (itr.hasNext()) {
// とりあえず格納しておく
X x = itr.next();
// 次があるならまだ最後の要素じゃない
if (itr.hasNext())
results.add(normalProc.apply(x));
// 無いなら最後の要素ということ
else
results.add(lastProc.apply(x));
}
return results;
}

// 配列版
public List loop(X args) {
return loop(Arrays.asList(args));
}

// テスト用メインメソッド
public static void main(String
args) {
// 普通の処理では、引数の後ろに”, ”を追加する
Loop looper =
new Loop(new Function() {
public String apply(String str) {
return str.concat(", ");
}
});
// 最後の処理では、引数をそのまま返す
looper.setLastProc(new Function() {
public String apply(String str) {
return str;
}
});
// String配列に対してループを実行
List results = looper.loop(new String {
"hoge", "piyo", "foo", "bar"
});
// hoge, piyo, foo, barと出力される
for (String result : results)
System.out.print(result);
}
}


こんな感じで、結果の出力のループはごく単純なものになる。
まぁ、この程度だったらループ内にロジックを書いても問題ないけどね。

String strs = {"hoge", "piyo", "foo", "bar"};
for (int i = 0; i < strs.length; i++) {
if (i == strs.length - 1)
System.out.println(strs[i]);
else
System.out.print(strs[i].concat(", "));
}

・・・わかりにくいね*3。ただ、Loopクラス*4にはチェックされる例外を外に出せないという欠点がある*5

それにしても、内部クラスに限ってはprivate abstractを許して欲しいような。

*1:抽象化?なんか違う

*2:と思う

*3:だからといって、Loopクラスを使うとわかりやすいかというと、少し疑問

*4:というかFunctionインターフェイス?

*5:これの対策は面倒なので、RuntimeExceptionでラップするといいよ(ぇ