uehaj's blog

Grな日々 - GroovyとかGrailsとかElmとかRustとかHaskellとかReactとかFregeとかJavaとか -

Groovyの型の話

最近また、Groovyを触り始めているのですが、いいですねこれは。数年前のClassic版しか経験がなかったのですが、速度的にも、エラーメッセージとかもずいぶん改善されています。


Groovyについて改めて何が良いと思うかというと、一つは、型付けをするプログラミングスタイルと、型付けをしないスタイルを使い分けられるところ。まあJavaと混用する以上必然なのかもしれませんが。


具体的にはまずは、変数宣言に型を指定できること。たとえば、変数に型の無いスタイル

def a = "abc";

だけではなく、

String s = "def";

とも指定できます。こうするとEclipseのGroovyプラグインでメソッド・フィールド名入力補完が効くようになります*1。良し悪しではありますけどね。


他には、例えば、'as'キーワードを使って、クロージャでインターフェースを実装することが出来ます。
たとえば、以下は1つのメソッドをもったインターフェースの場合。

interface A {
void foo(int a, int b, int c){}
}

c = { a,b,c -> println "this is a closure: $a, $b, $c" } as A;
c.foo(1,2,3);
//以下が出力される
//this is a closure: 1, 2, 3

複数のメソッドを持ったインターフェースの場合は、

interface B {
void foo(int a, int b, int c);
void goo(int a, int b, int c, int d);
void goo(int a, int b, int c);
}
d = {Object[] a -> println "this is a closure: $a" } as B;
d.foo(1,2,3);
d.goo(1,2,3,4);
d.goo(1,2,3);
//以下が出力される
//this is a closure: {1, 2, 3}
//this is a closure: {1, 2, 3, 4}
//this is a closure: {1, 2, 3}

のように、1つのクロージャで全てのメソッド呼び出しをまかなうようになる*2

マップでインターフェースを実装することも出来ます。

x = [foo: {int a, int b, int c -> println "this is a map" } ] as A;
x.foo(1,2,3);

内部的には、なんらかのラッピングクラス(マップをAにみせかけるもの)が生成されているようです。


後は余談です。

  • クロージャ、マップのいずれの場合でも、インターフェースではなく、クラスに対してasすること、つまり、実装を継承することは出来ません。
  • ちなみに、groovyでは、JavaScriptでそうであるように、マップはもともとオブジェクトと似た存在です。たとえば、

a = [f:{println "this is f";}]
(a.f)()

でfが呼び出せます。ただし、演算子の優先度の問題から、
(a.f)のように括弧でくくる必要があります。
もっと普通のオブジェクトのように呼びたい場合は、

a = [f:{println "this is f";}] as Expando
a.f()

のようにExpandoクラス*3にasすればOK。この場合、

def x = [ a:{println "this is a(c=$c)"; b(); },
b:{println "this is b"},
c:"hoge"] as Expando
x.a()
//以下が出力される
//this is a(c=hoge)

のように、クロージャ中から、自オブジェクトの持つ他のプロパティ(この場合c)への参照を行ったり、他のメソッドの呼び出しを行ったりすることも可能となります。

  • 前述の「マップ as インターフェース」の場合、このような「Expando化」はされません(つまりクロージャから他のプロパティやメソッドの参照が出来ない)ので、不便な場合があるかもしれません。

参考リンク:
http://groovy.codehaus.org/Groovy+way+to+implement+interfaces

*1:実はs = "abc"でもそれ以降のメソッド内ならsをStringとして認識して補完が効く。データフロー解析してるんですかね。

*2:しかしこれは、強引じゃないか・・。呼ばれた側として実際にどのメソッドして呼ばれたかがわからないし。

*3:しかしExpandoってなんて読むんだろう。エキスパンドゥー?「コマンドー」みたいなものか。