プロパティとフィールドとメソッド
- nobusueの日記: Grails Code Reading 第13回資料更新しました (http://d.hatena.ne.jp/nobusue/20081128/p1)
- オブジェクトインスタンスのクラス名
- obj.class.name
- オブジェクトのプロパティ/メソッド/フィールド一覧
- obj.properties
- obj.class.methods.name
- obj.class.fields.name
- オブジェクトの文字列表現
- obj.dump()
Pythonではクラスの変数もメソッドも全て属性(オブジェクト)で区別はないが、Groovyではプロパティ、フィールド、メソッドが明確に分かれている模様。
class Foo { def foo() {} def bar() { println this.class.methods[14].class } } new Foo().bar() //実行結果 class java.lang.reflect.Method
結局、メソッドは、java.lang.reflect.Methodであり、クロージャは、org.codehaus.groovy.runtime.MethodClosureであることが分かった。一応メソッドもオブジェクトとして取得可能。「.」でアクセスできるのはプロパティとフィールドだけで、メソッドは恐らく不可能。「.&」でメソッドをアクセスするとクロージャとして取得可能。
メソッドをドットでアクセスできるようにするとプロパティと区別が付かないから、メソッドに直でアクセスできないのだろうか?「メソッド=プロパティ」として値としてクロージャを持っているとすれば問題ないような気がするけど、もっと深い問題はあるのだと思う。
そもそもJavaと互換性を持つというのはかなりの制限だとは思うが、もちろんかなりのメリットはあると思う。Groovyは互換性があることが一番の売りだというイメージあるので無理だと思うが、PyPyであればJVMコードにも変換できるので、基本的に互換性を持たせようとすると良い言語ができないのではないだろうか?昔のC++ではないが、とりあえずGroovyをBetter Javaとして使用するというのは良いかもしれない。
おまけ:
class Foo { aaa // 何も指定しないとエラー final prop1 static prop2 int prop3 def prop4 public fld1 protected fld2 private fld3 def prop5 = {} public fld4 = {} protected fld5 = {} private fld6 = {} def meth1() {} public meth2() {} protected meth3() {} private meth4() {} } def f = new Foo() println f.properties println f.class.methods.name println f.class.fields.name
クラスメンバのプロパティ、フィールド、メソッドの区別のルールは以下の通り
- public、protected、privateを指定するとフィールド(ただしメソッドは除く)
- defを指定するとプロパティ
- 何も指定しないとエラー
- final、staticは区別に影響を与えない
- int、Stringなどの型の指定は区別に影響を与えない
- 値がクロージャの場合も区別に影響を与えない
- メソッド(xxx meth() {}の形式)であれば、def、public、protected、privateの指定には関係なくメソッドとなる
クロージャのレキシカルスコープ
インスタンス変数とローカル変数の参照するときの書き方は同じなので、クロージャのレキシカルスコープは非常に混乱しやすいと思った。例えば以下の例。まずはaをローカル変数とした場合。foo内のaはローカル変数で、f.a=2のaはプロパティということだと思う。
class Foo extends Expando { def foo() { a=1 bar = {println a} bar() } } def f = new Foo() f.a=2 f.foo() //実行結果 1
aをインスタンス変数とした場合。foo内のaもf.a=2のaも両方プロパティということだと思う。
class Foo extends Expando { def a def foo() { a=1 bar = {println a} bar() } } def f = new Foo() f.a=2 f.foo() //実行結果 2
インスタンス変数の参照で、「this.」を省略できるのでローカル変数と区別がない。Pythonでは必ず、「self.」を書かなくてはならないので区別が容易。またJavaとの互換性問題かな。
メソッドの動的追加
- Grな日々(uehajの日記): Expandoとは何か (http://d.hatena.ne.jp/uehaj/20080323/1206242516)
コメントで教えてサイトでExpandoに関して多少理解できたが、相変わらず混乱する。ルールをきちんと理解する必要があると感じた。以下のようにExpandoを継承すると動的にメソッド(のようなもの。実際はクロージャ)を追加できる。
class Foo extends Expando { def a def foo() { a=1 bar() } } def f = new Foo() f.bar = {println a} f.foo() //実行結果 1
以下のようにExpandoの特性を持っていないとプロパティbarにアクセスできない。
class Foo { def a def foo() { a=1 bar() } } def f = new Foo() f.bar = {println a} f.foo() //実行結果 groovy.lang.MissingPropertyException: No such property: bar for class: Foo at Foo.setProperty(Script77) at Script77.run(Script77:9)
Expandoを継承するとOKだが、以下のようにbarをプロパティとするとエラー。
class Foo extends Expando { def a def bar def foo() { a=1 bar() } } def f = new Foo() f.bar = {println a} f.foo() //実行結果 java.lang.NullPointerException at Foo.foo(Script79:6) at Foo$foo.call(Unknown Source) at Script79.run(Script79:11)
何でだろう?実行時エラーではなくコンパイル時のエラー?