ラムダ式とは一体何なのか

この記事は最終更新から3ヶ月以上が経過しています。情報が古い可能性があります。

Java8から使えるようになったラムダ式はAndroidではそのままでは使えません。Android Studioが「ラムダ式で書いたらこうなる」と見た目だけ表示してくれたりしますが、実際にラムダ式でコードが記述されているわけではありません。

例えば、このようなボタンにクリックリスナーを設定するコードがあったとして、

mButton.setOnClickListener(new View.OnClickListener(){
    @Override
    public void onClick(View v){
        Log.d(TAG, "Click!!");
    }
});

Android Studioがラムダ式スタイルで見た目をすっきりさせてくれるわけです。

mButton.setOnClickListener((v) -> { Log.d(TAG, "Click!!"); });

これはエディタ上で折りたたまれて表示されているだけで、実際に記述されているコードは上のものです(マウスカーソルを上に持って行くと表示されます)。

で、これを本当にラムダ式で記述できるようにするライブラリとしてretrolambdaというものがあります。あるんですが、その前に、そもそも私はラムダ式自体がよく分かっていません。そこでまずは、ラムダ式とはなんぞやというところから調べることにしました。

ざっくりした書き方で、私の中のイメージを書き連ねたのでわかりにくいところがあると思います。間違ってるところもあると思いますが、その際はご指摘いただけるとうれしいです。(長い前置き終わり)

ラムダ式はいろいろ省略することのできる記法

ラムダ式はアロー演算子を利用して(引数) -> {処理}という書き方ができるものです。この書き方ができるのは一定の条件下においてですが、引数にOnClickListenerのような抽象メソッドが1つだけのinterfaceを取る場合と考えておけばいいと思います。

OnClickListenerは以下のように、onClickという抽象メソッドが1つだけ定義されたインターフェースです。

    public interface OnClickListener {
        /**
         * Called when a view has been clicked.
         *
         * @param v The view that was clicked.
         */
        void onClick(View v);
    }

これをsetOnClickListener()する際に、無名クラスとして定義して使っているわけですが、その無名クラスの定義をすっ飛ばして、直接抽象メソッドへの引数と処理だけを書くことができるのです。

なぜ省略できるか

なぜ省略して書けるかというと、まずsetOnClickListener()というメソッドは、引数にOnClickListenerというインターフェースをとります。そしてJavaのinterfaceという仕組みによって、onClick()というメソッドが必ず実装されていることが保障されます。すなわち、この中では少なくともonClick()というメソッドが呼ばれることがわかっているわけです。だから省略できるのです。

ラムダ式の左辺については、引数が1つであれば()を省略できたり、型を省略することができます。型を省略できる理由は、インターフェースの定義で型が決められているため、省略されても分かるからです。

右辺の{}はメソッドの中身が1文ですむ場合に省略可能です。また、return文だけですむ場合も同様で、さらにreturn句も省略できます。なぜならソッドの戻り値がインターフェースの定義で決められており、右辺の処理が戻り値を表していると自明だからです。

あくまでこれらは「省略できる」であって、別に省略せずに書いても問題ありません。(もっともわざわざラムダ式を使う目的を考えれば、省略できるところは省略すべきでしょうが)

それでも分からない人に、もしかしたら引っかかるかもしれない情報

省略して書けることは分かったけど、やっぱりラムダ式よく分からない。そんな人は、「そもそもなぜメソッド1つだけが定義されたinterfaceを用意して使っているのか」が分かっていないことが原因かもしれません(私はそうでした)。

ラムダ式が適用できるパターンが全てそうかは知りませんが、少なくともOnClickListenerについてはObserverパターンによる実装です。なぜメソッド1つだけのinterfaceを定義して使うかというと、「クリックされた」というイベントを監視するのに便利だからです。

イベントの発生を監視するためには、イベントの発生を通知する人が必要になります。そのときに監視側が「俺はnowClickメソッドで通知してくれ」、「僕にはclickメソッドで教えて」、「私はonClickメソッドで」とバラバラな実装になっていたらどうでしょう。こうなるとイベントを通知するクラスは、イベントを監視するクラスが増える度に通知処理を書き換える必要性に追われます。そんなのはナンセンスです。

そこで「うるせー! クリックイベントは今後OnClickListenerというインターフェースのonClickメソッドで通知する。異論は認めん!」と通知する側が決めてしまえば全てがすっきりします。

イベント通知側は監視者が誰であるかを気にする必要がなくなります。どんなクラスが通知を受けたがっているのか、どんなメソッドで通知して欲しいのかを考えなくてもよくなるのです。イベントが発生したときにインターフェースを介して通知すればいいだけなのですから。

受け取る側は、指定されたインターフェースを実装しさえすれば、イベント発生時にそのメソッドを通じてイベントの発生を検知することができるわけです。誰がイベントを通知してくるのかを考えなくても良くなります。実際、AndroidにおいてonClickメソッドを誰が通知してくるのか考えなくても処理できています(Androidがよしなにやってくれてるんでしょう)。

それをスマートに実現する方法が、メソッド1つだけ定義したinterfaceを使うことです。

私自身まだふわっとした理解なので、余計に混乱させてしまったら申し訳ないです・・・。

まとめ

  • ラムダ式は省略記法
  • OnClickListenerのような抽象メソッド1つのインターフェースで使える
  • そういうインターフェースはObserverパターンで使われる
  • なぜならイベントの発生・監視に便利だから

ラムダは単なる省略記法というわけではないのでしょうが、とりあえず今は単純に割りきって考えようと思います。単なる省略記法と思えば、ラムダ式への心理的なハードルがぐっと下がりました。最初の一歩としては「省略できるんだ」でいいんじゃないかと思います。ただし変な覚え方して後々困ることになるのかもしれませんが・・・。

ちなみにそんなラムダ式をAndroid Studioで使えるようにするライブラリが、gradle-retrolambda – GitHubです。(導入の仕方とかはここでは語りません、よく分かってないので)

参考

知っといてムダにならない、Java SE 8の肝となるラムダ式の基本文法 (1/3)

矢沢久雄の早わかりGoFデザインパターン(6) 第6回 Stateパターン/Observerパターン

RxAndroidとRetrolambdaで大体Java8をAndroidに持ち込む

ご意見ご感想はこちら

お名前 (必須)

メールアドレス (必須)

メッセージ本文

メッセージを送信するにはチェックを付けてください。