Groovy:this.metaClass与instance.metaClass相对应

Guo*_*eng 8 groovy metaclass this

我在书中遇到了下面的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()
Run Code Online (Sandbox Code Playgroud)

它的输出如下:

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
Run Code Online (Sandbox Code Playgroud)

所以我很困惑:

  1. 为什么this.metaClass不等于instance.metaClass?
  2. 更进一步,我不能使用this.metaClass来注入新方法; groovy告诉我this.metaClass没有这样的属性,我打算注入.
  3. "org.codehaus.groovy.runtime.HandleMetaClass@245b4bdc [groovy.lang.MetaClassImpl@245b4bdc [class Person]]"是什么意思?我知道"245b4bdc"可能是对象指针.但是为什么HandleMetaClass和MetaClassImpl具有相同的指针值"245b4bdc"?

目前,我发现@ 245b4bdc不是" 对象引用 ",因此HandleMetaClass @ 245b4bdc不一定与MetaClassImpl @ 245b4bdc相同.我们可以使用Object.is()方法来判断它们是否相同.(我这样做,结果是假的)

Guo*_*eng 4

  1. 为什么 this.metaClass != instance.metaClass ?

    它涉及groove对字段的访问。

    • 当从“外部”访问实例字段时,groovy 实际上调用函数 getFieldName()。在我的例子中,当我使用“ instance ”时,我在外面;因此,instance.metaClass将调用instance.getMetaClass()。

    • 当从“ inside ”访问实例字段时,groovy 只是直接访问该字段,而不会调用 getFieldName() 。在我们的例子中,当我使用“ this ”时,我在“ inside ”;所以“ this.metaClass ”将直接访问“ metaClass ”。

    • 最后,getMetaClass()返回一个HandleMetaClass对象,而内部metaClass是一个MetaClassImpl对象。所以this.metaClass!=instance.metaClass。

  2. 为什么 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()
Run Code Online (Sandbox Code Playgroud)

代码示例(针对问题 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)
Run Code Online (Sandbox Code Playgroud)