Javaラムダ式
今更な気がするが、実務で使えなかったからきちんと考えこむことが出来なかった。
今やっと使えるようになったので、まとめる。
ラムダと言われるとラムダ・ドライバを真っ先に思い浮かべる。
ブラックボックス化しているわけではなく、きっちりと読めるし理解も出来る。
必要性
なくても実装は問題ない。
使わなくても実装することはできる。
しかし、チームで開発する場合、書ける人はいるもの。
その際にラムダ式が全く読めないでは、大問題になる。
書けるようになったからには読めるようにしておかないと、Javaエンジニアとして失格。
ラムダ式の知識は必要不可欠!
ラムダ式
Java8で導入された内部クラスを簡易的に書ける記法。
Java8導入された関数型インターフェース*1の実装を簡単にできるようになる。
基本形
引数と処理を->でつなぐだけ
( 引数 ) -> { 処理 }
以下、関数型インタフェースのConsumerの実装例。
Java7以前
Consumer<String> cons = new Consumer<String>() { @Override public void accept(String str) { System.out.println(str); } };
インタフェースなので、当然インスタンスはnewするだけでは出来ない。
抽象メソッドを実装してあげる必要がある。
Java8以降
Consumer<String> cons = ( str ) -> { System.out.println(str) };
異様にシンプル化する。
いろいろなモノが省略されている。
省略できるようになっているのは、自明であるため。
以下理由付きでの説明
最初のうちは、読めない事のほうが多い。
慣れてしまうとラムダ式で書かれた方がしっくり来るし、楽。
まずは、冗長でもいいから記述して、そこからラムダ式を適用する訓練をする。
そうすると次第に覚える。
ラムダ式の変数のスコープ
ラムダ式のキャスト
Java8では型推論が強化された。
型に変換する手がかりが何か1つでもあれば、推論可能!
ラムダ式は、関数型インタフェースである必要があるので、型は推論可能なハズ。
型を特定できる要素があれば、型解決してくれる。
Runnable r = () -> System.out.println("俺のターン!");
Runnableに特定出来るので、コンパイルエラーにならない。
しかし、型が特定出来ないような状態だとコンパイルエラーが発生する。
Object r = () -> System.out.println("俺のターン!");
Object型はすべてのクラスが継承しているので型が特定できない。
この場合は、キャストを噛ませると型が特定できるので、コンパイルエラーが発生しなくなる
Object r = (Runnable) () -> System.out.println("俺のターン!");
実際にこんな使い方をするか疑問であるが、裏の動きをしるために重要
交差型キャスト
「(インターフェース名1 & インターフェース名2 & …)」という形で、キャストするときに「&」でインターフェース名を繋いでいく。
参照型のオブジェクトの指定も可能。
その場合は、つ目の型は参照型で,二つ目以降の型はインターフェースでなければいけない。
参考: JavaSE8リリース記念!マイナーな言語仕様を紹介してみる(交差型キャスト,レシーバパラメータ(仮引数にthis)) - きつねとJava!
マーカーインタフェース(Serializableなど)との併用していくのが正解???
使用例
Supplierに文字出力の実装をしたインスタンスを作り、バイト配列としてByteArrayOutputStream, ObjectOutputStreamを使って出力する。
その後、バイト配列にした時と逆の動きをして、Supplier にキャストして動作した時にどうなるかを実証。
Supplier s = (Supplier<String> & Serializable) () -> "満足させてくれよ"; byte[] byteArray; try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) { try (ObjectOutputStream oos = new ObjectOutputStream(bos)) { oos.writeObject(s); } byteArray = bos.toByteArray(); } catch (Exception e) { throw e; } try (ByteArrayInputStream bis = new ByteArrayInputStream(byteArray)) { try(ObjectInputStream ois = new ObjectInputStream(bis)) { Supplier readObject = (Supplier)ois.readObject(); System.out.println(readObject.get()); } }
結果
満足させてくれよ
満足できました!
つまりは、文字列だけでなく関数も出力可能ってわけですね。
驚愕の事実