第二段。今更ラムダです。
職場で使ったことがないのであんまりピンとこないんですがこちらの神サイト
を参考に勉強してみました。
#とりあえず動かそう
とりあえず一番簡単なものをを書いてみました。
/* 例1。引数なしの簡単なやつをとりあえず動かしてみる */
Runnable runner = () -> {
System.out.println("初ラムダ式。今の所何が凄いのか分かってない。");
};
Thread t = new Thread(runner);
t.start();
これを実行するとよくわからないけど標準出力のところに書いたメッセージがコンソールに出ます。
書き方としては引数 -> 処理 って書くみたいですね。
この例では引数がないのでピンとこないのですが、引数がある時は
(s) 型省略
(String s) 型も変数名も書く
s 変数名だけ書く
とかいろいろできる。()まで省略できるのは引数が1個だけの時のみらしいです。
例①だけだとピンとこないのでもう少しやってみる。
/* 例2。引数ありの場合 */
List<String> l = new ArrayList<>();
l.add("hello");
l.add("world");
// ラムダっぽくしてみたぞ
l.forEach(s -> {
System.out.println("loop:" + s);
});
なんとなくラムダっぽいですね(謎)。実行すると
loop:hello
loop:world
と出ます。
いきなりforEachメソッドの引数の所にラムダ式が出てきていて、最初、書いておきながら????
ってなりましたが、forEachメソッドの引数はConsumerっていう標準の@FunctionalInterfaceで、
このConsumerの実装を記述してあげてると思えば良さそう。(FunctionalInterface=関数型インターフェースについては後述)
なので、Consumerの実装クラスをちゃんと用意して、こんな風にも出来る。
/**
* ConsumerIFを実装してみたクラス。
* @author komikcomik
*
*/
public class ConsumerImpl implements Consumer<Object> {
@Override
public void accept(Object t) {
System.out.println("Consumer実装したよ:" + t);
}
}
これをこんな感じで呼び出す。
/* 例3。Consumerを実装した、自前のFunctionalInterface(関数型インターフェース)を使用した場合。もはやラムダではないが */
l.forEach(new ConsumerImpl());
実行すると以下のように出ます。
Consumer実装したよ:hello
Consumer実装したよ:world
例2の方がラムダ式使った感を満喫できますね(使った感で選ぶのもどうかって思いますが)。
例1だけだと匿名クラスの書き方変えたバージョンくらいにしか思えず、
あまり開発現場で使うイメージがわかなかったのですが
例2,3のように関数型インタフェースを引数にとるjava標準のメソッドで、
引数IFの実装を書くっていうならまあまあ使い所がありそうに思えます。
しかし理屈は分かっても使い慣れるまでには時間がかかりそう。
#関数型インターフェース
さっき途中で出てきたやつです。
細かい条件はこちらの神サイトを参照していただければと思いますが、適当に説明すると
抽象メソッドが1つだけ定義されているインターフェースのことです。
このIFはラムダ式やメソッド参照(まだ私の投稿では扱ってないけど)の代入先になれる存在みたいです。
なので、ラムダとかのJava8要素を理解しようと思ったら関数型IFの理解は必須な感じですね。
関数型IFとなれる条件は書いたとおり簡単なので自作してもいいけど、Java8では
・Supplier・・・引数無しで値を返す
・Consumer・・・引数ありで値は返さない(なにか処理をする)。さっきの例でも出てきたやつ。
・Predicate・・・引数ありでtrue/falseを返す
・Function・・・引数ありで、任意の型を返す(プリミティブ型用のもあるよ)
みたいに色々標準的な関数IFを提供しているらしいので、
変に自作するよりはすでに標準提供されているものを選んだほうが開発プロジェクトに新規参画した人には「なんですかこのオリジナルの関数型IF」ってならなくて良いかもしれない。