groovysh では def は使わないほうがいい

id:fumokmm さんが書かれていた

groovyshではdefは使わないほうがいい(たぶん)
が気になったので、それが 『たぶん』 ではなく 『真実』 であることを書いてみた。

検証環境は Groovy 1.7.5, JVM: 1.6.0_20

真実 その 1...

groovysh は実行単位で インスタンスが異なる。

groovy:000> this;
===> groovysh_evaluate@20dccfab
groovy:000> this;
===> groovysh_evaluate@2e831a91
真実 その2...

宣言した変数は インスタンス変数ではない。
→ 他のインスタンスからは読み取れない。

groovy:000> def a = 0; this.a;                                    
ERROR groovy.lang.MissingPropertyException:
No such property: a for class: groovysh_evaluate
        at groovysh_evaluate.run (groovysh_evaluate:2)
        ...

上記の例外を見る限り 変数 a は groovysh_evaluate.run() のローカル変数だと思われる。

と言うことで...
def で宣言した変数は 同じ行 (実行) でしか参照できない。
ではなぜ def を使わない場合は

groovy:000> a = 10
===> 10
groovy:000> a
===> 10
のように 2回目の実行でも 変数 a が参照できるのだろうか?

真実 その3...

def をつけずに宣言した変数は binding される。(詳細は こちら
→ binding された変数は 同一 Shell 内で参照できる。

groovy:000> a = 10
===> 10
groovy:000> binding['a']
===> 10
groovy:000> binding.variables.each { if (it.key ==~ /^a.*/) println it }
a=10

更に、id:orangecloverさんが書かれた

Groovyでdef関数からdef関数が呼び出せないのか?
というのも この binding が関係している。

真実 その4...

メソッドもクロージャも binding される。
→ 同一 Shell 内であれば 呼び出せる。

groovy:000> void a1() { println a }
===> true
groovy:000> def a2() { println a }
===> true
groovy:000> a3 = { println a }    
===> groovysh_evaluate$_run_closure1@41e8e144
groovy:000> binding.variables.each { if (it.key ==~ /^a.*/) println it }
a=10
a1=org.codehaus.groovy.runtime.MethodClosure@190872ce
a2=org.codehaus.groovy.runtime.MethodClosure@79a93f38
a3=groovysh_evaluate$_run_closure1@41e8e144
真実 その5...

メソッド内では binding 変数が参照できない。
→これは 不具合っぽいけど...

groovy:000> a1();  
ERROR groovy.lang.MissingPropertyException:
No such property: a for class: groovysh_evaluate
        at groovysh_evaluate.a1 (groovysh_evaluate:2)
        at groovysh_evaluate.run (groovysh_evaluate:2)
        ...
groovy:000> a2();
ERROR groovy.lang.MissingPropertyException:
No such property: a for class: groovysh_evaluate
        at groovysh_evaluate.a2 (groovysh_evaluate:2)
        at groovysh_evaluate.run (groovysh_evaluate:2)
        ...
真実 その6...

クロージャ内では binding 変数が参照できる。

groovy:000> a3();
10
===> null

と言うことで 結論!!
groovysh を利用する場合は...

  • def は使わないほうがいい
  • メソッドよりクロージャを使ったほうがいい