バージョン1.7.5現在、Groovyではアクセス修飾子が機能しません。これらはコメント同様、注意を促す役割しか持ちません。次の例を見てください。アクセス修飾子がその名を偽っていないのなら、このクラスの全てのフィールドとメソッドへのアクセスは制限されるはずです。
----
package foo
class Foo {
protected protectedField
@PackageScope packagePrivateField
private privateField
protected protectedMethod() { }
private privateMethod() { }
}
----
Groovyが期待どおりにGroovyコードをJavaバイトコードにコンパイルしているか確認する為に、classファイルを逆アセンブルしてみましょう。
----
javap -private Foo
----
結果はこうなりました。問題ありません。完璧です。期待どおり。
----
public class Foo extends java.lang.Object implements groovy.lang.GroovyObject{
protected java.lang.Object protectedField;
java.lang.Object packageScopeField;
private java.lang.Object privateField;
:
protected java.lang.Object protectedMethod();
private java.lang.Object privateMethod();
}
----
しかしこれらへのアクセスは制限されません。次の例のようにどこからでもアクセスできます。
----
package bar
def foo = new Foo()
foo.protectedField
foo.packagePrivateField
foo.privateField
foo.protectedMethod()
foo.privateMethod()
----
http://jira.codehaus.org/browse/GROOVY-1875によればこのバグは2.0までに直るようです。では今できることは?
命名規則はどうでしょうか? 事故から守るにはこれでほぼ充分かもしれません。例えばPythonやPerlのようにアンダースコアのプレフィクスを使います。
----
protected _protectedField
@PackageScope __packageScopeField
private ___privateField
protected _protectedMethod()
private ___privateMethod()
----
汚いし、複雑です。使いたくありません。
クロージャはどうでしょうか? もし必要となるのがprivateスコープだけなら、クロージャを使うことで実現できます。
----
package foo
class SecretiveFoo {
SecretiveFoo() {
def privateField;
getPrivateField = {
privateField
}
setPrivateField = { newValue ->
privateField = newValue
}
def privateMethod = {
println 'private method called'
}
publicMethod = {
privateMethod.call()
// do others
}
}
public getPrivateField
public setPrivateField
public publicMethod
}
----
----
package bar
def secretiveFoo = new SecretiveFoo()
secretiveFoo.setPrivateField(1)
println secretiveFoo.getPrivateField()
secretiveFoo.publicMethod()
----
結果はこうなります。
----
1
private method called
----
ただし、このやり方には高いコストがかかります。次のプログラムを動かすとわかります。Transparentは通常のGroovyクラスです。Serectiveはこのやり方をシミュレートするクラスで、指定された数だけprivateフィールドにアクセスするクロージャを生成します。これらを1000回インスタンス化するコストを計測します。
----
class Secretive {
Secretive(n) {
def privateField = 0
actions = []
(0..n).each {
actions << { privateField }
}
}
def actions
}
class Transparent {
private privateField = 0
}
def maxmem = Runtime.runtime.maxMemory()
def memBefore = maxmem - Runtime.runtime.freeMemory()
def timeBefore = System.nanoTime()
def list = []
(0..<1000).each {
list << new Secretive(10)
// list << new Transparent()
}
def timeAfter = System.nanoTime()
def memAfter = maxmem - Runtime.runtime.freeMemory()
def diff = memAfter - memBefore
println "time usage: ${ (timeAfter - timeBefore) * 0.000001 } ms"
println "memory usage: ${ (memAfter - memBefore) * 0.001 } kb"
----
次の結果が出ました。重すぎます。使えません。
Transparent | Secretive(10) | Serective(20) | Serective(30) | |
---|---|---|---|---|
Average Time Usage (ms) | 17.26 | 62.83 | 75.66 | 87.15 |
Average Memory Usage (kb) | 1133.67 | 2706.19 | 3720.96 | 5073.95 |
2.0のリリースをひたすら待つしかないんでしょうか。
誰か案あります?
No comments:
Post a Comment