BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ ニュース JavaのSAMbdas

JavaのSAMbdas

原文(投稿日:2010/07/20)へのリンク

最初のラムダ提案がリリースされて(そして InfoQの詳細な分析)以来、その後の ラムダの状況 は、JDK 7のラムダ プロジェクトのゴールポストがずいぶん動かされた、と言える。

最初提案されたシンタックスは、ずいぶん批判されたが、問題は、純粋なシンタックス(それは、結局たたき台にすぎなかった)より、もっと深いものになった。主要な問題のひとつは、Javaが関数タイプを直接サポートすることができない;サポートしようとすると、Javaの型システムに穴をあけてしまうことになった(関数配列がリークする例外を許可してしまうことになる)。これらの問題が乗り越えられるかどうかにかかわらず(あるいは、それどころか、JDK 7のリリースが増々遅れるそうな既定の時間枠内では)、現在のラムダの状況は、関数タイプには全く触れていない、と言える。

その代わりに、インナークラスを書くのが簡単になる。これらのクラスは、SAMクラス、すなわちSingle Abstract Method クラスと呼ばれる。これは、Java言語において、1つの抽象メソッドのみを持つ抽象クラスやインターフェースの重要なサブセットを表わしている;例えば、Runnablerun()メソッドやComparatorcompare() メソッドなどである。(1つの抽象メソッドを持つ抽象クラスも許可されている。例えば、Eclipseの org.eclipse.core.runtime.jobs.Job。)

現在、作業中のスペックでは、以下の2つは,同じになる:


Collections.sort(list,new Comparator() {
  public int compare(Object o1, Object o2) {
    return(o1.toString().length() - o2.toString().length());
  }
}
// 次と同じ
Collections.sort(list,
  { Object o1, Object o2 -> o1.toString().length() - o2.toString().length() }
);

またしても、シンタックスは、提案の段階で,将来変わる可能性がある。しかしその考えでは、ラムダ プロジェクトでは、インナークラスが、新しい匿名クラスのアプローチを使うより、もっと簡潔に書けるようになっている。さもないと、ラムダは、インナークラスと同じ表現力を持ったままである;ローカルのヒープから状態をキャプチャできる(キャプチャ後に、そのヒープが変更可能のままかどうかは、今だに熱く議論されている話題であるが)。しかし言語におけるいくらかの変更(例えば、finalな変数を効果的にキャプチャできるようになること)とタイプやメソッド/例外の推論によって、等価な匿名クラスよりもずっと簡潔になる。

このアプローチに従う理由の1つは、既存のクラス(主に java.util collectionsクラス)を修正しないで済むことである。もし関数タイプのアプローチが採用されたら、ラムダが使えるように、collections クラスも修正の必要が出てくる、あるいは、JDK 7では、ラムダを含める(よって使う)ことを諦めることになる。他のライブラリは、柔軟にできたのに、一枚岩の Javaライブラリは、それほど変更しやすくない。このために、違うアプローチを選ぶことになったわけである。

SAMbdaの代わりに、メソッド参照を使うことも可能だったかもしれない。例えば、次のように:


public class Comparisons {
  public static int compareLength(Object o1, Object o2) {
    return(o1.toString().length() - o2.toString().length());
  }
  public static int compareHash(Object o1, Object o2) {
    return(o1.hashCode() - o2.hashCode());
  }
}
// 例
Collections.sort(list,#Comparisons.compareLength);
Collections.sort(list,#Comparisons.compareHash);

# 参照は、メソッド ハンドルで java.lang.reflect.Methodに似ている。しかし Methodと違って、コンパイル時(実行時ではなく)に解決され、そして、 JVMの JITは、非常にスマートなので、メソッド参照を自動的にインライン化できる。また他の最適化もあり、それによって、使う時に新しい匿名クラスを生成するのではなく、あるSAMタイプのデリゲートされたメソッドのハンドルを表す単一クラスを生成することができる。

最後に、異論のある問題 もある。現行スペックの最初のドラフトでは、 breakcontinueの使用を禁じていた。しかし,その後に、break と continueを使っても、 SAMBdaからbreakしても包含スコープにならないことが,明らかになった。他の主な変更は、return が暗黙となった、そしてラムダ自身の本体にあることは,許されない;替りのキーワード yieldThread.yield()と混乱しないように)は、インナークラスにおける return と正確に,同じ意味である。表面上は、これで、ラムダがメソッドから戻るトリガを与えることができ、メソッド内でそれは後で呼ばれる(いわゆる'long return')。将来,シンタックスの変更で、return がラムダ内で修飾なしで使えるかもしれない。そしてその後に新しいキーワード(あるいは,long returnのようなキーワードの組合せ)が必要かもしれない。他に似たような話が、包含SAMのインスタンスを参照するのに thisを含むことである。 Outer.this は、包含クラスのインスタンスを参照する。

ラムダ提案が暫定的なSAMに変更になったのは、プロジェクトの最初の提案に比べて、野心的なものではないが、実装が簡単になったのと、即役に立つように既存のcollectionクラスに変更が必要でないことは、有利な点である。(どんな場合でも、もし関数タイプが加わったら、collectionクラスへの変更が必要になる。)将来、同じラムダ風のシンタックスを使って、関数参照を生成することも可能かもしれないが、将来のJDKのリリースになるだろう。

この投稿は、 アップデートされ、break/continueが解決されことを反映し、そして仮引数と例外の型推論のことも含んでいる。

この記事に星をつける

おすすめ度
スタイル

BT