LoginSignup
33
33

More than 5 years have passed since last update.

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

Last updated at Posted at 2014-10-11

はじめに

GroovyがAndroid上で実行可能に
とか、
Groovy and Android: a winning pair by Cédric Champeau
とか、
Groovy On Android
を見て、GroovyでAndroidを書いてみたくなりました!Groovyにより記述されたパワフルなAndroid向けライブラリもたくさん出てくるでしょう。Groovyにより冗長だった記述がより簡潔になると思います。Groovy 2.4の正式リリースが待ち遠しいです。(投稿執筆時は、Groovy2.4-rc3が最新)

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

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

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

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

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

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

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

 興味がわいた方は、プログラミングGROOVYなどの分かりやすい本や、Groovy詳しい方のブログで勉強するのが良いと思います。

Groovyにはクロージャがある

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のクロージャの利用例
assert [1, 2, 3].collect({Integer num -> return 2 * num}) == [2, 4, 6]

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

引数の型を省略できる

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

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

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

defキーワードで変数を宣言
def message = "Hello"
println message // Helloと表示
defキーワードでメソッドをの返り値型と引数の型を宣言
def get0(){
    return 0
}

void printMessage(def message){
    println message
}

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

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

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

クロージャの引数の型いろいろ
// 型を付ける
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別冊:第8章 Groovy 2.0の新機能での解説がとても分かりやすいです。

returnも省略できる

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

クロージャの省略
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がある

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

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

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

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

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

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

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

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

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

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

このclosWithItと

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

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

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

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

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

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インターフェースとして渡すことができます。

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

 以下は、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のリリースノート)

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

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!") }
setOnClickListenerで強制型変換+クロージャを使う
button.setOnClickListener({ Log.v(TAG, "On Clicked!") })

 簡潔になりましたね。

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

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

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

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

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

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

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

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

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で書いた例。

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

 GroovyOnAndroidの例です。

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以上を使っていることが前提です。

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)

まとめ

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

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

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

33
33
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
33
33