【问题标题】:Groovy: this.metaClass Versus instance.metaClassGroovy:this.metaClass 与 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

所以我很困惑:

  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() 方法来判断它们是否相同。(我这样做了,结果是 false

【问题讨论】:

  • 如果你把它改成assert this.class.metaClass == instance.metaClass,它就会通过。
  • 为什么? this.class.metaClass == Person.metaClass 应该吗?
  • 在这里找到了更好的答案,stackoverflow.com/a/45407488/42769,其实就是我。

标签: groovy this metaclass


【解决方案1】:
  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

  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()

代码示例(对于问题 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 子类中定义,所以我想知道字段访问是如何完成的。
猜你喜欢
  • 2018-01-06
  • 2016-03-23
  • 1970-01-01
  • 1970-01-01
  • 2012-12-05
  • 1970-01-01
  • 2015-12-10
  • 2010-09-07
  • 1970-01-01
相关资源
最近更新 更多