これは、JSR-335 専門家グループで最も困難で広範囲に議論された決定の 1 つです。一方では、単一の抽象メソッドの抽象クラスがラムダの適切な変換ターゲットになる可能性があることは完全に合理的であるように思われます。そして、あなたのメンタルモデルが「ラムダは単なるコンパクトな匿名クラス」である場合、これは完全に合理的な考えでした。
ただし、この文字列をしばらく引っ張ると、少数のユースケースのために、多くの複雑さと制約が発生することに気付きます。
これが引きずる最悪のことの 1 つは、ラムダ本体内の名前の意味であり、特殊なケースとして、this
. 内部クラスの本体内には、内部クラス内の名前がスーパータイプのメンバーを参照したり、レキシカル環境からキャプチャされたりする可能性があるため、非常に複雑な検索規則 (「くし型検索」) があります。(たとえば、多くのバグや謎解きthis
は、Outer.this
、内部クラス本体で。) 抽象 SAM クラスへのラムダ変換を許可した場合、2 つの不適切な選択肢があります。内部クラスのひどい名前検索の複雑さですべてのラムダを汚染するか、抽象クラスターゲットへの変換を許可しますが、ラムダ本体が基本クラスのメンバーを参照できないようにアクセスを制限します (これは、独自の混乱を引き起こします)。結果として得られるルールは非常にきれいです。ラムダ パラメーターの形式を除いてthis
、ラムダ本体内の名前 (単なる名前である を含む) は、ラムダ本体のすぐ外側で意味するものを正確に意味します。
ラムダを内部クラスに変換する際のもう 1 つの問題は、オブジェクト ID とそれに伴う VM 最適化の損失です。内部クラス作成式 (例: new Foo() { }
) は、一意のオブジェクト ID を持つことが保証されています。ラムダのオブジェクト ID にそれほど強くコミットしないことで、VM を解放して、他の方法では行うことができなかった多くの有用な最適化を行うことができます。(結果として、ラムダ リンケージとキャプチャは、匿名クラスよりも既に高速です。さらに、まだ適用していない最適化の深いパイプラインが残っています。)
さらに、単一の抽象メソッドの抽象クラスがあり、ラムダを使用してそれらを作成できるようにしたい場合は、これを有効にする簡単な方法があります。関数インターフェイスを引数として受け取るファクトリ メソッドを定義します。ThreadLocal
(これを行う Java 8 のファクトリ メソッドを追加しました。)
「ラムダはオブジェクトにとって便利な構文にすぎない」という世界観の棺桶の最後の釘は、既存のコードベースと単一抽象メソッド インターフェイスと抽象クラスの使用の分析を行った後に得られました。抽象クラスに基づいているのはごくわずかな割合であることがわかりました。すべてのラムダに、使用の 1% 未満しか恩恵を受けないアプローチの複雑さとパフォーマンスの問題を負わせるのはばかげているように思えました。そのため、他の 99% 以上のメリットを享受するために、このユース ケースを切り離すという「勇敢な」決定を下しました。