Create a gist now

Instantly share code, notes, and snippets.

// helper
def shouldFail(Class klass, Closure clos) {
try {
clos.call()
} catch (Throwable e) {
assert e.class == klass : "expected: ${klass.name}, but actual: ${e.class.name}"
return
}
assert false : "expected: ${klass.name}, but nothing thrown"
}
// traitとクラスの定義
trait Flyable {
String name = "trait"
String fly() { "I'm $name and flying!" }
}
class Bird implements Flyable {
}
class UFO {
}
// 普通に静的にtraitを適用する
def b = new Bird()
assert b.fly() == "I'm trait and flying!"
b.name = "bird"
assert b.fly() == "I'm bird and flying!"
// 実行時にtraitを適用する
def ufo = new UFO()
def ufoF = ufo as Flyable
assert ufoF.fly() == "I'm trait and flying!"
ufoF.name = "UFO"
assert ufoF.fly() == "I'm UFO and flying!"
shouldFail(MissingMethodException) {
// もちろん元オブジェクトには影響ない
ufo.fly()
}
// 元クラスに対するProxyクラスが作られてるみたい。
assert ufo.class != ufoF.class
assert ufoF.class.name ==~ /UFO[0-9]+_groovyProxy/
// -> "UFO<通番>_groovyProxy"のようなクラス名が生成されている
// 通番部部分は、同一JVM上で実行するたびにインクリメントされてる風
// 実行時適用するたびに新たなインスタンスが生成される。
def temp = []
temp << (ufo as Flyable)
temp << (ufo as Flyable)
temp << (ufo as Flyable)
assert temp.unique().size() == 3
temp*.dump().each { println it }
// <UFO55_groovyProxy@5ef0c85a $closures$delegate$map=[:] $delegate=UFO@464e19f6 Flyable__name=null>
// <UFO55_groovyProxy@4c2c5287 $closures$delegate$map=[:] $delegate=UFO@464e19f6 Flyable__name=null>
// <UFO55_groovyProxy@7083dc29 $closures$delegate$map=[:] $delegate=UFO@464e19f6 Flyable__name=null>
// プロキシクラスに対してメタクラスをいじっても、当然元のクラス/インスタンスには影響しない
ufoF.metaClass.hoge = {-> println "HOGE" }
ufoF.hoge()
shouldFail(MissingMethodException) {
ufo.hoge()
}
// $delegateプロパティで変換前のオブジェクトを参照できる。
// 元のオブジェクトとの同一性判定がどうしても必要な場合も一応どうにかできる。
assert ufoF.$delegate == ufo
shouldFail(ReadOnlyPropertyException) {
// $delegateはReadOnlyプロパティ
ufoF.$delegate = ""
}
// Proxyオブジェクト経由だと自分自身のメソッドへの呼び出しはProxyの仕組みを通らないのでNGになったりする実装もあるけど大丈夫?
trait ChainCall {
def a() { b() }
def b() { c() }
def c() { d() }
}
class ChainCallClass1 implements ChainCall {
def d() { "Goal!" }
}
class ChainCallClass2 {
def d() { "Goal!" }
}
assert new ChainCallClass1().a() == "Goal!"
assert (new ChainCallClass2() as ChainCall).a() == "Goal!"
// ->大丈夫
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment