12
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ラムダ式のちょっとした使い道

Last updated at Posted at 2016-09-04

Java1.8でストリームやラムダに対応し、遅まきながら世間の流れである関数に手を出した昨今です。
関数いいですね。うん。
オブジェクト指向を知ったときに感じた感動と同じような感動を感じます。

とにかく処理が短くなる。見やすくなる。明確になる。バグがでにくくなる。
そんな印象です。
で、私が今こんな事に使っていますというちょっとした事を一つ、まとめて書いてみました。


Lambda constructor

たとえば、引数が沢山あるデータエンティティ(Entity)があるとします。
それをインスタンス化してリスト化して返したいとします。

sample1
Entity suzuki = new Entity("suzuki",30,......);

どの引数が何の値なのかさっぱりわからない・・・

sample2
Entity suzuki = new Entity();
suzuki.setName("suzuki");
suzuki.setAge(30);
suzuki...
suzuki...
suzuki...
//and so on....

普通はこうするんでしょうけど、これも嫌いなんです。

sample3
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;

こういうのを見ると、頭を抱えてしまいます。
見るからにバグの温床の気がします。そこで

sample4
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);

こんな風に書けたらいかがでしょうか?

sample5
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.

今度はコンストラクタではなくセッターです。
値をセットするだけ。

sample6
AAAAAAAAFactory factory = context.getFactory();
factory.setMax(100);
factory.setMin(10);
factory.setInitial(10);

AAAAAAAAFactoryなんて知りたくもないじゃないですか?

sample7
MyObjects.set( context.getFactory() , f -> {
   f.setMax(100);
   f.setMin(10);
   f.setInitial(10);
});

このほうが分かりやすいと思うのですよ。
もしセットしたいものが一つならこうしますよね?

sample8
context.getFactory().setMax(100);

いくつ値をセットする時でも、同じ事がしたいのです。


実装

MyObjects.java
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ではダメで、むしろ汚くなった気がします。という事で諦めました。

どなたか良い案があれば教えて下さい。

12
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?