Groovy:this.metaClass 与 instance.metaClass
Posted
技术标签:
【中文标题】Groovy:this.metaClass 与 instance.metaClass【英文标题】:Groovy: this.metaClass Versus instance.metaClass 【发布时间】:2016-03-30 17:01:42 【问题描述】:我在书中遇到过下面的 groovy 脚本代码。它给我带来了一些奇怪的输出。
class Person
def work()
println "work()"
def sports=['basketball','football','voleyball']
def methodMissing(String name, args)
if(name in sports)
println "injected $name into Person class"
Person instance=this
println "this.metaClass:\t\t$this.metaClass"
println "instance.metaClass:\t$instance.metaClass"
assert this.metaClass==instance.metaClass
else
println "no such method:$name() in Person class"
def jack=new Person()
jack.football()
输出如下:
injected football into Person class
this.metaClass: groovy.lang.MetaClassImpl@245b4bdc[class Person]
instance.metaClass: org.codehaus.groovy.runtime.HandleMetaClass@245b4bdc[groovy.lang.MetaClassImpl@245b4bdc[class Person]]
Caught: Assertion failed:
//I did not paste the detailed assertion here for simplicity
所以我很困惑:
-
为什么 this.metaClass 不等于 instance.metaClass?
还有,我不能用this.metaClass来注入新方法; groovy 告诉我 this.metaClass 没有我打算注入的此类属性。
“org.codehaus.groovy.runtime.HandleMetaClass@245b4bdc[groovy.lang.MetaClassImpl@245b4bdc[class Person]]”是什么意思?我知道“245b4bdc”可能是对象指针。但是为什么 HandleMetaClass 和 MetaClassImpl 的指针值相同“245b4bdc”?
目前,我发现@245b4bdc 不是“对象引用”,所以 HandleMetaClass@245b4bdc 不一定是与 MetaClassImpl@245b4bdc 相同的实例。我们可以使用 Object.is() 方法来判断它们是否相同。(我这样做了,结果是 false)
【问题讨论】:
如果你把它改成assert this.class.metaClass == instance.metaClass
,它就会通过。
为什么? this.class.metaClass == Person.metaClass 应该吗?
在这里找到了更好的答案,***.com/a/45407488/42769,其实就是我。
【参考方案1】:
为什么 this.metaClass != instance.metaClass?
它涉及到 Groove 对字段的访问。
当从“outside”访问实例字段时,groovy 实际上调用了函数 getFieldName()。在我的示例中,当我使用“instance”时,我处于外部;所以 instance.metaClass 会调用 instance.getMetaClass()。
从“inside”访问实例字段时,groovy 直接访问该字段,不会调用getFieldName()。在我们的示例中,当我使用“this”时,我处于“inside”;所以“this.metaClass”会直接访问“metaClass”。
最后,getMetaClass() 返回一个 HandleMetaClass 对象,而内部的 metaClass 是一个 MetaClassImpl 对象。所以this.metaClass!=instance.metaClass。
为什么 this.metaClass.say=->println "say" 会抛出 MissingPropertyException?
this.metaClass 的类型是 MetaClassImpl
MetaClassImpl 是一个底层类,它支持上层类(例如HandleMetaClass)进行注入。不适合开发者直接使用,所以不支持注入方式:xxxx.say=->println "say"。
代码示例(针对问题 1):
class Person
def work()
println "work()"
def sports=['basketball','football','voleyball']
def methodMissing(String name, args)
if(name in sports)
Person instance=this
println "this.metaClass:\n\t$this.metaClass"
println "instance.metaClass:\n\t$instance.metaClass"
//output: false
println "this.metaClass.is(instance.metaClass):\n\t$this.metaClass.is(instance.metaClass)"
//output: true
println "this.getMetaClass().is(instance.getMetaClass()):\n\t$this.getMetaClass().is(instance.getMetaClass())"
else
println "no such method:$name() in Person class"
def jack=new Person()
jack.football()
jack.football()
代码示例(对于问题 2):
class Cat
def a=new groovy.lang.MetaClassImpl(Cat)
try
a.say=->println "say"
catch(MissingPropertyException e)
println "[Fail]\n\tcan not inject method say() into MetaClassImpl class.\n"
def b=new org.codehaus.groovy.runtime.HandleMetaClass(a)
println b
b.say=->println "[say]"
println "[OK]\n\tcan inject method say() into HandleMetaClass class\n"
def method=b.getMetaMethod("say")
method.invoke(this)
【讨论】:
那么当当前类中没有定义metaClass
时,它是如何进行字段访问的呢?也许它的访问修饰符在其Object
超类中受到保护?但无论如何,根据 groovy 文档,为了获得字段访问权限,需要从定义它的类中访问该属性。 metaClass
没有在 Person 子类中定义,所以我想知道字段访问是如何完成的。以上是关于Groovy:this.metaClass 与 instance.metaClass的主要内容,如果未能解决你的问题,请参考以下文章