Java1.8でストリームやラムダに対応し、遅まきながら世間の流れである関数に手を出した昨今です。
関数いいですね。うん。
オブジェクト指向を知ったときに感じた感動と同じような感動を感じます。
とにかく処理が短くなる。見やすくなる。明確になる。バグがでにくくなる。
そんな印象です。
で、私が今こんな事に使っていますというちょっとした事を一つ、まとめて書いてみました。
Lambda constructor
たとえば、引数が沢山あるデータエンティティ(Entity)があるとします。
それをインスタンス化してリスト化して返したいとします。
Entity suzuki = new Entity("suzuki",30,......);
どの引数が何の値なのかさっぱりわからない・・・
Entity suzuki = new Entity();
suzuki.setName("suzuki");
suzuki.setAge(30);
suzuki...
suzuki...
suzuki...
//and so on....
普通はこうするんでしょうけど、これも嫌いなんです。
List<Entity> list = new ArrayList<>();
Entity e = new Entity();
e.setName("suzuki");
e.setAge(30);
e...
e...
e...
e.....
//and so on...
list.add(e);
e = new Entity();
e.setName("suzuki");
e.setAge(30);
e...
e...
e...
e...
e.....
//and so on...
list.add(e);
return list;
こういうのを見ると、頭を抱えてしまいます。
見るからにバグの温床の気がします。そこで
Entity suzuki = MyObjects.instance(Entity::new , t -> {
t.setName("suzuki");
t.setAge(30);
t...
t...
//and so on.
});
Entity yamada = MyObjects.instance(Entity::new , t -> {
t.setName("yamada");
t.setAge(30);
t...
t...
//and so on.
});
return Arrays.asList(suzuki,yamada);
こんな風に書けたらいかがでしょうか?
return Arrays.asList(
MyObjects.instance(Entity::new , t -> {
t.setName("suzuki");
t.setAge(30);
t...
t...
//and so on.
}),
MyObjects.instance(Entity::new , t -> {
t.setName("yamada");
t.setAge(30);
t...
t...
//and so on.
})
);
正直、sample5にする必要はあまり感じません。asList
がどこまで繋がっているか分かりにくいと思います。
個人的にはsample4が好きです。
そういえば、fluentタイプってのもありましたね。
Entity suzuki = EntityBuilder.new()
.setName("suzuki")
.setAge(30);
Entity yamada = EntityBuilder.new()
.setName("yamada")
.setAge(30);
return Arrays.toList(suzuki,yamada);
うん。美しいですね。ですが、こんなビルダーを全てのエンティティに用意している人はいないのではないでしょうか。
そういえばLombokに@Builderありますね・・・どうなんでしょう。
(しかし、よくよく考えると、setterの戻り値をvoidではなくthisを戻す決まりにしてしまえば、これ普通にできるのでは・・)
Lambda setter.
今度はコンストラクタではなくセッターです。
値をセットするだけ。
AAAAAAAAFactory factory = context.getFactory();
factory.setMax(100);
factory.setMin(10);
factory.setInitial(10);
AAAAAAAAFactoryなんて知りたくもないじゃないですか?
MyObjects.set( context.getFactory() , f -> {
f.setMax(100);
f.setMin(10);
f.setInitial(10);
});
このほうが分かりやすいと思うのですよ。
もしセットしたいものが一つならこうしますよね?
context.getFactory().setMax(100);
いくつ値をセットする時でも、同じ事がしたいのです。
実装
public class MyObjects{
/**
* lambda constructor.
*/
public static <T> T instance(Supplier<T> sup, Consumer<T> init){
T t = sup.get();
init.accept(t);
return t;
}
/**
* lambda setter.
*/
public static <T> void set(T t, Consumer<T> init){
init.accept(t);
}
}
ほとんど何もありません。
最後に
大したコードではないと思いますし、ラムダのおかげで凄い事ができたというわけでもありません。
ですが、私がこれまでJavaで嫌いだった様々な標準的なイディオムを消し去る事ができて、ラムダ様様です。
わき道
Entity suzuki = MyObjects.instance(Entity::new, t -> {
t.name = "suzuki";
t.age = 30;
});
Entityを変更せずにこう書けたら最高に幸せです。
@Data
public class Entity{
protected String name;
protected int age;
@FunctionalInterface
public interface Initializer extends Cunsumer<Entity>{}
}
こんなエンティティを定義して
Entity suzuki = MyObjects.instance(Entity::new, (Entity.Initializer) t -> {
t.name = "suzuki";
t.age = 30;
}
こんなことは出来ないわけではありません。でもこれ、さすがにアクセス修飾子がprivateではダメで、むしろ汚くなった気がします。という事で諦めました。
どなたか良い案があれば教えて下さい。