Closureという概念が理解しにくいので色々やってみた@Groovy - (define -ayalog '())につけた長文コメントを整理しておこうかと思って書いた。
動作確認は Groovy2.0.0 です。2.0.0 って言ってみたかった。でもこの辺って多分1.7とかと変わってないと思う。
コード
とりあえずコピペ。
def counter(){
int i=0
return {
i++
}
}
「メソッドの返り値は何?」
counterメソッドのreturnは { i++ } です。
これはクロージャのインスタンス作ってるののシンタックスシュガーです。日本語でおk。でも日本語で言っても伝えられる自信が全くない。だからコードで書くね!
return { i++ }
Closure c = new Closure(null) {
Object call() {
return i++;
}
}
return c;
なるべくJavaっぽく書いてみた。これに置き換えても動きます。
で、呼び出しについて。
println c()
println c.call();
Closureのcall呼び出すのって多いので、省略していきなり実行出来るように見せかけてるだけです。だから省略してるのとしてないのの組み合わせでも同じ挙動。
def c = {'a'}
assert c() == 'a'
assert c.call() == 'a'
assert c.doCall() == 'a'
うん。よく使うものは省略しとこうぜーなのがGroovyだと思ってだいたい間違いない。だから普通は一番上の一番短い書き方する。
クロージャは引数とります。受けたいだけ並べれば良いです。仮引数名を -> の前に書くです。
def c = { a, b, c ->
a + b + c
}
assert c(1,2,3) == 6
これ省略すると、引数1つをとるクロージャになります。何も書かなきゃ0個…ではなく、1個なんですね。なお、省略時の仮引数名は it になります。アレです。
def c = { println it }
c()
c(1)
見ての通り、呼ぶ方は省略出来ちゃうので紛らわしい。引数1個のクロージャを引数0個で動かすために無理矢理 null 突っ込んで呼んでるだけです。
0個にしたいときは0個にする必要があります。 -> の前に何も書かない。これで0個になります。
def c = {-> 'abc'}
assert c() == 'abc'
仮引数を明示的に指定して作ったクロージャに引数渡すと例外になります。0個で作ったら1つでも渡すと例外だし、3つで作って2つしか渡さなかったら例外です。
仮引数を省略して作ったクロージャに引数2つ渡すと例外になります。0個か1個です。
まとめ
Groovyは色々省略出来るけど、どうしてそう省略できてるかをある程度把握しないと、何でそうなるのか理解出来なくて困ることもたまにあります。たまに。……結構?たぶん、たまに。
かといって省略せずにやるとGroovyの力は半減どころじゃないですし……使ってるうちにわかるもんですかねー。
なお、ここで書いてるのは現時点での私の理解であり、間違ってるところとか誤摩化してるところとか多分あります。ごまかしはニヨニヨしといてください。間違ってたらこっそり教えてください。