GroovyOnAndroidやRxJavaをやりたい人のための、Groovyのクロージャの書き方・使い方メモ

# はじめに

「[GroovyがAndroid上で実行可能に](http://www.infoq.com/jp/news/2014/06/groovy-android)」
とか、
「[Groovy and Android: a winning pair by Cédric Champeau](https://speakerdeck.com/melix/groovy-and-android-a-winning-pair)」
とか、
「[Groovy On Android](http://melix.github.io/blog/2014/06/grooid.html)」
を見て、GroovyでAndroidを書いてみたくなりました！Groovyにより記述されたパワフルなAndroid向けライブラリもたくさん出てくるでしょう。Groovyにより冗長だった記述がより簡潔になると思います。Groovy 2.4の正式リリースが待ち遠しいです。(投稿執筆時は、Groovy2.4-rc3が最新)


また、
「[関数型言語を学ぶことは実務でどう役に立ったか](http://rejasupotaro.github.io/2014/05/11/47.html)」
とか、
「[iOSのSwiftとAndroidのGroovy](http://rejasupotaro.github.io/2014/06/05/49.html)」
を読んで、RxJavaにも興味が出てきました。ただRxJavaをJava 6やJava 7でやる場合、コールバックを記述する箇所がちょっと冗長になりそうですね。Java 8でやるぶんにはラムダ式があるから良いのでしょうが。RxJavaをAndroidで使うならば、Groovyと合わせて使うとより簡潔に読みやすく記述できそうですね。(Java 7などでラムダ式を使える[RetroLambda](https://github.com/orfjackal/retrolambda)というのもあるようですが．．．)

さて、GroovyOnAndroidのサンプルコードなどでこんなコードが出てきます。

```groovy:GroovyOnAndroidの例
button.setOnClickListener { Log.v(TAG, "Clicked Button!") }
```

RxJavaのwikiにこんな感じのコードが載っています。


```groovy:RxJavaの例
Observable.from("Taro", "Jiro", "Saburo").subscribe{
    println "Hello " + it + "!"
}
```

　Groovyになじみの無い方は、「なんだコレ？」とか、「あれView.OnClickListenerじゃないの？」とか思ったかもしれませんね。実はこれらのGroovyのコードでは、クロージャを使っているのです。

　この投稿ではGroovyのクロージャの書き方、読み方、使い方を一部簡単ではありますが紹介します。GroovyOnAndroidやRxJavaのサンプルやwikiを見る際の助けになれば幸いです。

　興味がわいた方は、[プログラミングGROOVY](http://gihyo.jp/book/2011/978-4-7741-4727-7)などの分かりやすい本や、Groovy詳しい方のブログで勉強するのが良いと思います。

# Groovyにはクロージャがある

Groovyにはクロージャがあります。Groovy公式ページ(日本語約)の「[クロージャ](http://groovy.codehaus.org/Japanese+Closures)」によると

> Groovyのクロージャは「コードブロック」やメソッドポインタのようなものです。ひと固まりのコードとして定義され、後になって実行されます。

とのことです。

クロージャの基本的な書き方は、

```groovy:基本的なクロージャの書き方
{ 引数のリスト -> 処理 }
```

です。

いくつか例を示します。

```groovy:クロージャの生成と呼び出しの例
// println はSystem.out.printlnのショートカット
// Groovyではセミコロンを省略可

// シンプルなクロージャ
Closure clos = { println "Hello Closure" } // Closureインスタンスの生成
clos.call() // クロージャの呼び出し Hello Closureと表示
clos() // 普通はこっちの書き方で呼び出す

// 引数のあるクロージャ
Closure closWithArg = { String message -> println message }
closWithArg("Hello Closure") // Hello Closureと表示

// 返り値のあるクロージャ
Closure closReturnObject = { -> return "Hello Closure" }
println closReturnObject() // Hello Closureと表示

// 返り値と引数のあるクロージャ
Closure closWithArgReturnObject = { String message -> return message }
println closWithArgReturnObject("Hello Closure") // Hello Closureと表示
```

　普通は上記のようにクロージャを生成してすぐには使いません。クロージャを引数にとるメソッドと一緒に使います。またコールバックやリスナーを登録するためにも使います。

```groovy:Groovyのクロージャの利用例
assert [1, 2, 3].collect({Integer num -> return 2 * num}) == [2, 4, 6]
```

　ちなみに上記の「クロージャの生成と呼び出しの例」と「Grooovyクロージャの利用例の書き方」のような書き方はあまりしません。もっと短いクロージャの書き方があります。

# 引数の型を省略できる
　次のように引数の型を省略して書くことも可能です。

```groovy:引数の型を省略したGroovy
Closure clos = { message -> println messagge}
```

　Groovyにはdefというキーワードがあります。defは変数、メソッドの返り値型、メソッドの引数の型に指定可能です。defを指定した場合、Objectを指定しているのと同じです。

```groovy:defキーワードで変数を宣言
def message = "Hello"
println message // Helloと表示
```

```groovy:defキーワードでメソッドをの返り値型と引数の型を宣言
def get0(){
	return 0
}

void printMessage(def message){
	println message
}
```

　また、メソッドの引数の場合は、defすら省略することが可能です。

```groovy:defを省略したメソッド
def printMessage(message){
	println message
}
```

　クロージャの引数でも同じように、任意(オプショナル)に型を付けた書き方もできますし、defで指定することもできますし、defも省略し型を書かない書き方もできます。

```groovy:クロージャの引数の型いろいろ
// 型を付ける
Closure clos0 = { String message, String name -> println "${message} ${name}" }
clos0 ("Hello", "Ryota")

// defで指定
Closure clos1 = { def message, def name -> println "${message} ${name}" }
clos1 ("Hello", "Ryota")

// defも省略し型を書かない
Closure clos2 = { message, name -> println "${message} ${name}" }
clos2 ("Hello", "Ryota")
```

　ちなみに、Groovy2.0から静的型チェックと静的コンパイルが追加されました。[プログラミングGROOVY別冊：第８章 Groovy 2.0の新機能](http://beta.mybetabook.com/b/ksky/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0GROOVY%E5%88%A5%E5%86%8A%EF%BC%9A%E7%AC%AC%EF%BC%98%E7%AB%A0+Groovy+2.0%E3%81%AE%E6%96%B0%E6%A9%9F%E8%83%BD)での解説がとても分かりやすいです。


# returnも省略できる

　Groovyではクロージャやメソッドで、次のようにreturnが省略可能な場合があります。


```groovy:クロージャの省略
Closure clos0 = { Integer num -> return num * 2 }
println clos0(10) // 20と表示

Closure clos1 = { Integer num -> num * 2 }
println clos1(10) // 20と表示

Closure clos2 = { Integer num -> 
	if(num > 0) {
		num * 2
	} else {
		0
	}
}

println clos2(1) // 2と表示
println clos2(0) // 0と表示
println clos2(-1) // 0と表示

Closure clos3 = { -> 
	println "Hello! "
	0
}

def num = clos3() // Hello!と表示
println num // 0と表示
```

　Java8のラムダ式やC#のラムダ式では、ラムダ式の処理の文が一文の場合のみreturnを省略できますが、Groovyのクロージャでは文が複数あってもreturnを省略することが可能です。

# 暗黙の引数itがある

　こんな書き方ができます。

```groovy:itを使う
Closure clos = { println it }
clos("Hello Closure with it") // "Hello Closure with it と表示
```

　クロージャの基本的な書き方は次のようなものでしたね。上記の書き方では`->`がありませんね。

```groovy:基本的なクロージャの書き方
{ 引数のリスト -> 処理 }
```

　`->`を省略した場合、暗黙の引数itがあると見なされます。そのため、


```groovy:引数名を明示するものと、itを使うものの比較
Closure printMessage = { message -> println message }
printMessage("Hello")

Closure printMessageWithIt = { println it }
printMessageWithIt("Hello")
```

上記のように引数が1個のクロージャは、明示的に引数を書くこともできますが、暗黙の引数itを使って簡潔に記述することも可能です。

気をつけないと行けないのは、

```groovy:暗黙の引数itをとるクロージャ
Closure closWithIt = { println "Hello" }
closWithIt(null)
```

このclosWithItと

```groovy:引数をとらないクロージャ
Closure closWithNoArgs = { -> println "Hello" }
closWithNoArgs(null) // 実行時例外が発生する
```

このclosWithNoArgsは違うということです。closWithItは暗黙の引数itがあり引数を1個取るクロージャ、closWithNoArgsは引数を取らないクロージャです。

# asでSAMインターフェースに変換できる

　ここまでずっとGroovyのクロージャの書き方に関してでしたが、やっとここからAndroidとかRxJavaとかが絡んできます。

　Groovyのクロージャをasを使って、メソッドが一つだけ定義されたインターフェースに強制型変換することが可能です。Androidの[View.OnClickListener](http://developer.android.com/reference/android/view/View.OnClickListener.html)インターフェースの例を示します。

```groovy:asを使ってクロージャを強制型変換
View.OnClickListener listener0 = { View view -> Log.v(TAG, "On Clicked!") } as View.OnClickListener
View.OnClickListener listener1 = { view -> Log.v(TAG, "On Clicked!") } as View.OnClickListener
View.OnClickListener listener2 = { Log.v(TAG, "On Clicked!") } as View.OnClickListener
```

　もちろん直接、setOnClickListenerのメソッドにView.OnClickListenerインターフェースとして渡すことができます。


```groovy:setOnClickListenerでas+クロージャを使う
button.setOnClickListener({ Log.v(TAG, "On Clicked!") } as View.OnClickListener)
```

　以下は、Javaで記述した場合です。

```java:setOnClickListenerをJavaで
button.setOnClickListener(new View.OnClickListener(){
	@Override
	public void onClick(View v) {
		Log.v(TAG, "On Clicked!");
	}
});
```

　冗長な部分が多いですね。Groovyでクロージャを使って記述した場合、冗長な部分を削ることが可能で、非常に読みやすくなりますね。

　ちなみにまだ短くすることが可能です。

# Groovy2.2から暗黙的クロージャ強制型変換がある

　Groovy2.2から暗黙的クロージャ強制型変換という機能が追加されました。([Groovy2.2のリリースノート](http://groovy.codehaus.org/Groovy+2.2+release+notes))


　前の節ではasを書いていましたが、Groovy2.2からasを書く必要はありません。

```groovy:asを使ってクロージャを強制型変換
View.OnClickListener listener0 = { View view -> Log.v(TAG, "On Clicked!") }
View.OnClickListener listener1 = { view -> Log.v(TAG, "On Clicked!") }
View.OnClickListener listener2 = { Log.v(TAG, "On Clicked!") }
```

```groovy:setOnClickListenerで強制型変換+クロージャを使う
button.setOnClickListener({ Log.v(TAG, "On Clicked!") })
```

　簡潔になりましたね。

# Groovyは()を省略できる場合がある

　Groovyはメソッド呼び出しの`()`を省略できる場合があります。
メソッドの引数が1個以上で、かつトップレベルにある式のメソッド呼び出しの場合です。

　そのため次のように記述することが可能です。

```groovy:setOnClickListenerで強制型変換+クロージャを使う
// 括弧省略前
// button.setOnClickListener({ Log.v(TAG, "On Clicked!") })

button.setOnClickListener{ Log.v(TAG, "On Clicked!") }
```

　またGroovy 1.8から、メソッドチェイン関連で省略できる場合が増えたようです。


# 最後の引数がクロージャの場合こんな書き方もできる

　最後の引数がクロージャの場合、次のような書き方もできます。

```groovy:
def func(Integer num, Closure clos) {
    if(num > 0) {
        clos(num)
    } else {
        clos(0)
    }
}


// 普通の書き方
func(10, { println it })

// 最後の引数がクロージャの場合、()の外にクロージャを書ける
func(10){println it}

// インデントを変えれば制御構造のように
func(10){
    println it
}
```

# クロージャの書き方いろいろ。GroovyOnAndroidとRxJavaを例に

クロージャの書き方の例をいろいろ示します。

## GroovyOnAndroidで、View.OnClickListenerとView#setOnClickListener

　まずはJavaで書いた例。

```java:setOnClickListenerとView.OnClickListener
button.setOnClickListener(new View.OnClickListener(){
    @Override
    public void onClick(View v) {
        Log.v(TAG, "On Clicked!");
    }
});
```

　GroovyOnAndroidの例です。

```java:setOnClickListenerとView.OnClickListenerとクロージャ
button.setOnClickListener({View view -> Log.v(TAG, "On Clicked!")} as View.OnClickListener)

button.setOnClickListener({View view -> Log.v(TAG, "On Clicked!")}) // asを省略し、暗黙的クロージャの強制型変換

button.setOnClickListener{View view -> Log.v(TAG, "On Clicked!")} // () を省略

button.setOnClickListener{view -> Log.v(TAG, "On Clicked!")} // 引数の型を省略

button.setOnClickListener{ Log.v(TAG, "On Clicked!")} // 暗黙の引数itを使う(実際にitは使っていない)

button.setOnClickListener({ Log.v(TAG, "On Clicked!")}) // もちろんこういう書き方もできる
```

## RxJavaのAction1\<T\>とObservable\<T\>#subscribe

　RxGroovyを使っているか、Groovy 2.2以上を使っていることが前提です。

```groovy:RxJavaでクロージャ
@Grab(group='com.netflix.rxjava', module='rxjava-core', version='0.20.4')

import rx.Observable
import rx.functions.Action1

Observable.from("Taro", "Jiro", "Saburo").subscribe(new Action1<String>() {
    @Override
    public void call(String string) {
        System.out.println("Hello " + string + "!");
    }
});

Observable.from("Taro", "Jiro", "Saburo").subscribe({ String name -> println "Hello $name"} as Action1)

Observable.from("Taro", "Jiro", "Saburo").subscribe({ String name -> println "Hello $name"})

Observable.from("Taro", "Jiro", "Saburo").subscribe({ name -> println "Hello $name"})

Observable.from("Taro", "Jiro", "Saburo").subscribe({ println "Hello $it"})

Observable.from("Taro", "Jiro", "Saburo").subscribe { println "Hello $it"}
```

# 【おまけ】RxGroovyがやっていること
　Groovyは2.2から暗黙的クロージャの強制型変換が導入され、Javaのメソッドが一つのインターフェース(SAMインターフェース)を引数にとるメソッドで、変わりにクロージャを記述することができるようになりました。

　RxGroovyは、RxJavaを利用する際いくつかのSAMインターフェースを引数にとるメソッドで変わりにクロージャを引数に渡せるようにしたものです。これにより、Groovy 2.0とGroovy 2.1でも、クロージャを用いた簡潔な記述が可能です。

　それについて興味がある方はこちらで書いたので見てください。「[RxJavaをGroovyで使う。そしてRxGroovyがやっていること(ExtensionModule)](http://qiita.com/RyotaMurohoshi/items/3f1f3cde0e5c6785b2f1)」

# まとめ

　「GroovyOnAndroidやってみたい」、「RxJavaのwikiのあのコード、こういう風によめるのか」という方がいたら嬉しいです。

　間違いなどありましたら、指摘していただけると嬉しいです。

# ついでに、よかったらどうぞ！

* [AndroidStudio0.6.1とGroovy2.4-beta-1で、とりあえずGroovyでAndroidアプリを作ってみた。](http://qiita.com/RyotaMurohoshi/items/4e11acc347dbd766e913)
* [RxJavaをGroovyで使う。そしてRxGroovyがやっていること(ExtensionModule)](http://qiita.com/RyotaMurohoshi/items/3f1f3cde0e5c6785b2f1)
* [Javaのライブラリをチョロっと試すなら、Groovyで試すといいと思うんです！](http://qiita.com/RyotaMurohoshi/items/99475255e56f8fae712b)