関ジャバでさくらばさんにProject Lambdaの最新情報について話していただけました。
僕も以前調べていました。
Java SE 8に導入されるのは、ほんとにクロージャ!? - Fight the Future
http://jyukutyo.hatenablog.com/entry/20111121/1321891230
この頃から、変わった部分ももちろんあるので、それについてまとめてみます。
でも正直に言って、さくらばさんのブログを読んだほうが詳しいし、正確です。
Java Advent Calendar 1 日目 - Project Lambda の遅延評価
http://www.javainthebox.com/2012/12/java-advent-calendar-1-project-lambda.html
ここでは僕の備忘録代わりにまとめます。
Project LambdaつきJDK
Java Platform, Standard Edition 8 Early Access Releases — Java.net
相変わらず、JDK7 + Project Lambdaなので、Java SE 8のその他の新機能はついてません。
defaultキーワードの位置
void remove() default { throw new UnsupportedOperationException("remove"); }
とメソッド名の後ろだったのが、
default void remove() { throw new UnsupportedOperationException("remove"); }
となります。 ただし、2012/12/03現在のものでは、まだ以前の位置のままです。
Listインタフェースからmap()メソッドがなくなった?
以前は
list.map(x -> x * 2)
という風にバルク操作を呼び出せていました。 今はこれだとコンパイルエラーになります。
Streamの概念が取り入れられています。 Scalaにあるものと同様のものです。
Streamとは
Streamとはイテレータに似て、空になるまで値を引き出すことができます。しかし、実際に次の要素を要求しない限り、操作が行われることはありません。 つまり、遅延を実現するためのものです。これにより無限のリストを扱うことができます。
なので、
list.stream().map(x -> x * 2)
のようにStreamオブジェクトを取得して呼び出します。 複数の処理を連続して呼び出せます。
list.stream().map(x -> x * 2).reduce(0, (t, n) -> t + n)
ただし、map()メソッドは遅延するため、map()メソッドを呼び出した時に処理が実行されるません。実際には即時評価されるメソッドであるreduce()メソッドを呼び出した時にmap()引数として渡していた処理が実行されます。
Iterablesクラス
もともと、Listでmap()メソッドを呼び出せていたのは、java.util.Iterablesクラスを継承していたからでした。
もうListはIterablesクラスを継承していません。
もともと、Listでmap()メソッドを呼び出せていたのは、Iterableインタフェースにmap()メソッドのデフォルト実装があり、そこでユーティリティクラスであるjava.util.Iterablesクラスのメソッドに委譲していました。今はIterableインタフェースからバルク操作メソッドは取り除かれています。
Iterablesクラスはほとんどコメントアウトされていて、残っているのはcount()メソッドだけです。
インナークラスのclassファイルを生成しなくなった?
ラムダ式を書くと、それはSAMタイプ(Single Abstract Method)のシンタックスシュガーなわけで、今まではインナークラスのclassファイルが生成されていました。あの$がつくやつね。 しかし、今のバージョンでは、ラムダ式の処理をinvokedynamicを利用して呼び出します。
試しにラムダ式を書いて、「javap -v」します。 すると、出ました!invokedyamic!
58: invokedynamic #7, 0 // InvokeDynamic #0:lambda:()Ljava/util/functions/Mapper;
うーん、でもどうしてinvokedynamicを使うんだろう、と疑問に思いました。
すばらしいエントリがありました。
Lambda 式に invokedynamic を使うのかもしれない話 - miyakawa_takuの日記
http://d.hatena.ne.jp/miyakawa_taku/20120728/1343478485
Lambda 式は匿名クラスのインスタンス生成と同じバイトコードを生成します。これには性能面の欠点があります。たとえば、コンパイル時に Lambda 式ごとにクラスが作られるので、 Lambda 式をたくさん使うとクラスロードが大変になります。
なるほど…そしてinvokedynamicを使うことで、初回実行時に呼び出されるブートストラップメソッドが、匿名クラス方式か、MethodHandleの呼び出しにするのかを選択できます。 invokedynamicは呼び出す処理を実行時に変えられるからです。
MethodHandleは合成したり部分適用したりできるんですが、匿名クラスに比べれば呼び出しは遅くなります。そりゃそうか。