ラムダ式は必ずしも invokedynamic に翻訳されるわけではない

Java 8 で導入される新しい Date & Time API に関する良い記事です。 *1

ですが、冒頭のラムダ式に関する記述に若干の問題があります。

ひとつだけラムダ式について言及しておくと、「ラムダ式は(関数型インターフェースの)オブジェクトを生成する」と説明している文章があったら、その文章は怪しいので疑いの目で読んでください。実際にはラムダ式はオブジェクト生成のコードにはならないからです(InvokeDynamicの呼び出しコードになります)。

問題点は次の2点です。

  1. ラムダ式は関数型インタフェースのオブジェクトを生成 (produce) します。ただし、インスタンス化 (instantiate) するとは限りません。
  2. 現時点で OpenJDK と Oracle JDKコンパイラは、ラムダ式を invokedynamic に翻訳しますが、仕様でそのように要求されているわけではありません。実装依存です。

Java 言語仕様 (Java SE 8 版) 15.27 節 では、ラムダ式の評価について次のように記載されています。

Evaluation of a lambda expression produces an instance of a functional interface.

(拙訳: ラムダ式の評価は関数型インタフェースのインスタンスを生成します)

用語法について2点説明します。「評価 (evaluation)」は「実行」と言い換えられます。「生成 (produce)」は「10 + 20 という式を評価すると 30 という値が生成 (produce) される」のように使う動詞です。「戻す」と意訳したほうが通りが良いかもしれません。

つまりこの文は、「ラムダ式を実行すると関数型インタフェースのインスタンスが戻る」と言っています。ここでは、インスタンスを生成するための具体的な方法は定義されていません。したがってコンパイラは、毎回愚直にインスタンス化するバイトコードを吐くかも知れませんし、 invokedynamic を介して遅延初期化するスマートなバイトコードを吐くかも知れません。いずれにしても、関数型インタフェースのインスタンスが戻ることに変わりはないので、プログラムはちゃんと動きます。

実際、大昔の JDK は、毎回愚直にインスタンス化するバイトコードを吐いていました。現在のように invokedynamic を使うようになるために、言語仕様の変更は必要ありませんでした。具体的な動作は実装依存だからです。

ラムダ式を invokedynamic に翻訳する理由については、手前味噌ですが次の記事をおすすめします。

*1:ただし、 LocalDateTime / ZonedDateTime の使い分けには同意できません。タイムゾーン・時差の情報を含まない日時を表すためには、当然に LocalDateTime を使うべきです。「一緒のクラスにするほうが安全だと思う」というのは、あえて設計の意図に反する理由としては弱いでしょう。