BT

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

寄稿

Topics

地域を選ぶ

InfoQ ホームページ ニュース ラムダの現状

ラムダの現状

原文(投稿日:2011/12/16)へのリンク

Brian Goetz氏はラムダの現状をアップデートし公表した。これは、Java言語にラムダを追加する計画(JSR 335Java強化提案(Java エンハンスメント・プロポーザル、Java Enhancement Proposal)の番号126として追跡されているものでもある)についてのステータスレポートである。

Java言語にラムダを持ち込む計画は、任意の関数に対する文法と(Collectionsのような)Javaの既存のクラスセットをラムダを受け入れるメソッドで拡張する計画の両方をカバーする。そのゴールはmapfilterといった、コレクションをより関数型よりの方法で処理することができるようにする関数を追加することである。

これらを既存のインターフェースに組み込むために、defaultメソッドと呼ばれる新しいタイプのメソッドがインターフェースに追加された(かつての初期の版ではdefenderメソッドと呼ばれていたものだ)。これによってインターフェースがメソッドの実装を伝えることを事実上実現できる。defaultメソッドが呼び出されたとき、コンパイラは最初にインスタンスに委譲する。そのとき、メソッドが通常のメソッド解決の経路上で見つからなかった場合には、このdefaultメソッドが代わりに呼び出される。初期の版とは異なり、新しい仕様では、既存のクラスのstaticメソッドへのポインタとしてではなく、正当なインターフェースの構成要素としてメソッドを許容することにしている(フィールドはそうではない) – defaultキーワードで識別されるようにはなっているが。発表された文の中で示されている例はすべてのイテレータにskip()メソッドを追加するものである:

interface Iterator<E> {
  boolean hasNext();
  E next();
  void remove();

  void skip(int i) default {
    for (; i > 0 && hasNext(); i--) next();
  }
}

これは事実上Java言語に多重実装継承を追加することになるので、(同じdefaultメソッドが2つの別の経路で継承されるような)衝突の際には、そのメソッドはそのクラスでオーバーライドされるか、または、新しいIterator.super.skip()コンストラクトを利用して明示的に1つを選択しなければならない。

もう1つの最近の変更はメソッド参照に対する文法の変更提案であり、JavaDocに触発されたPerson#compareという形からよりC++の影響を受けたPerson::compareという形にするものである。文法がどうであろうと、メソッド参照は実質的にラムダがキャプチャするメソッドにあるメソッドを渡すための型チェックされたショートカットを提供する方法を提供する。

実際のラムダ表現はそれほど大きく変化していないが、文法の一部や術語のいくつかは最近改訂されている。例えば、以前の決定ではC#やScalaがラムダ表現の文法として使っている=>を使うことになっていたが、今は代わりに->という文法に更新されている。その結果、自身の否定を返す関数はint a -> a+1のように書けるだろう。(型が推測できるのであれば、その型が代わりに使われる。しかしこの表現の場合には、コンパイラは私たちがaとしてbyteshortintlongのいずれを意図しているかが分からないため、明示的に曖昧さを取り除く必要がある。)

もう1つの術語の変更はRunnableActionといったクラスの概念である。これらは単一の抽象メソッド(single abstract method)をもつインターフェースであり、以前SAM型と呼ばれていたものだ。Javaの中でそれらをより幅広く利用するとができるようにするため、いまではそれらは関数型インターフェース(functional interface)として知られている。この変更の理由はメソッド参照からアップキャスト可能なものとしての利用を促すことにある。その結果、Comparatorインスタンスを期待するメソッドをもつ既存のJavaコードベースに対して、ComparatorのcompareToメソッドと同じシグニチャをもつラムダ表現(またはメソッドハンドル)を渡すことができる。テクニカルには、Comparatorは2つの抽象メソッド(compareTo()とどういうわけかequals())を持っているけれども、equals()はすでにObjectで利用可能であるので、これを関数型インターフェースとみなすことができる。

関数型(fuction type)は検討され(初期のドラフトでは利用でき)たが、承認されなかった – 少なくとも、今のところは – これは、現バージョンのJVMに関数型イレイジャが持ち込む問題の難しさによるものである。将来導入されることがなくなったわけではないが、関数型よりも型ベースの関数型インターフェースの方がJavaと既存のJavaクラスの両方により直接的に有用であろうというのが、期待されていることである。

ラムダは引数の型を明示的に型付けしなくても推測され得るという利点は持ち続けている(上述のように、ある種の曖昧さの回避は必要だが)。また、ラムダは再帰可能であり、その内包するスコープの状態をキャプチャすることができる(内部クラスのように内包するスコープをキャプチャするラムダはクロージャとして知られている) – そのようなキャプチャは、こちらも内部クラスと同様に、final変数だけではあるが。しかしながら、effectively finalの導入によって、finalは多くの場面で推測可能となり、明示的に表現する必要がなくなる。

Java言語へのラムダとメソッド参照の追加は、一般的な演算に必要とされる定型コードの量を大きく減らすだろう。例えば、StringのArrayを大文字小文字の区別なくソートするためには、次のように書くことができる:

Arrays.sort(names, String::compareToIgnoreCase)

あわせて、(Scalaのtraitsのように)既存コードに影響を与えることなくインターフェースを拡張することができるdefaultメソッドにより、Javaを書くことはずっと簡単なことになるだろう。完全な関数型とオブジェクト指向のハイブリッドではないが、その基盤となるものはJDK8やその先で利用可能な関数型スタイルのライブラリの数の増加の中に見られるだろう。そして、ラムダとdefaultメソッドを使ってコンパイルされたコードはJDK8よりも古いものでは実行可能ではなくなるだろうが、JDKの古いバージョンでコンパイルされたコードは実行可能であるだろうし、ジェネリック型がゆっくりとJava言語い持ち込まれたのとほぼ同じように、関数型のやり方が使われるようになるだろう。

この記事に星をつける

おすすめ度
スタイル

BT