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() // 'null' c(1) // '1'
見ての通り、呼ぶ方は省略出来ちゃうので紛らわしい。引数1個のクロージャを引数0個で動かすために無理矢理 null 突っ込んで呼んでるだけです。
0個にしたいときは0個にする必要があります。 -> の前に何も書かない。これで0個になります。
def c = {-> 'abc'} assert c() == 'abc'
仮引数を明示的に指定して作ったクロージャに引数渡すと例外になります。0個で作ったら1つでも渡すと例外だし、3つで作って2つしか渡さなかったら例外です。
仮引数を省略して作ったクロージャに引数2つ渡すと例外になります。0個か1個です。
まとめ
Groovyは色々省略出来るけど、どうしてそう省略できてるかをある程度把握しないと、何でそうなるのか理解出来なくて困ることもたまにあります。たまに。……結構?たぶん、たまに。
かといって省略せずにやるとGroovyの力は半減どころじゃないですし……使ってるうちにわかるもんですかねー。
なお、ここで書いてるのは現時点での私の理解であり、間違ってるところとか誤摩化してるところとか多分あります。ごまかしはニヨニヨしといてください。間違ってたらこっそり教えてください。