2016-03-07 82 views
1

我想实现功能性,允许在不使用动态属性的map的情况下将未知属性添加到类中。在propertyMissing方法中添加属性时的奇怪行为

由于Groovy允许使用metaClass我在propertyMissing方法中使用它。

class Item { 
    def propertyMissing(String name, value) { 
     this.class.metaClass."$name" = value 
    } 
} 

但我遇到了一个奇怪的行为。

def i1 = new Item() 

i1.prop = "value" 
println i1.properties // [class:class Item] 
println i1.prop // null 

i1.metaClass.field = "555" 
println i1.properties // [prop:null, class:class Item, field:555] 
println i1.prop // null 

i1.prop = "value1" 
println i1.properties // [prop:value1, class:class Item, field:555] 
println i1.prop // value1 

另外,如果我尝试设置在例如prop它不会增加它了

def i1 = new Item() 
i1.metaClass.unkn = "1111" 

i1.prop = "value" 
println i1.properties // [class:class Item, unkn:1111] 
println i1.prop // null 

i1.metaClass.field = "555" 
println i1.properties // [class:class Item, unkn:1111, field:555] 
println i1.prop // null 

i1.prop = "value1" 
println i1.properties // [class:class Item, unkn:1111, field:555] 
println i1.prop // null 

为什么它有如此行为之前访问metaClass

回答

2

当您动态更新对象的元类时,Groovy会将元类替换为ExpandoMetaClass。这是MetaClass的一个特殊实现,它支持添加和删除属性/方法。

但是,在您的示例中,ItemGroovyObject,它在MetaClass上有一个持久字段。当交换MetaClass时,此字段不会更新:只有注册表中的元类由ExpandoMetaClass替换。这种类型的代码可以与javaobject一起工作,因为这个对象没有字段,并且每当groovy访问元类时都会执行解析类 - >元类。

在你知道你要一个Groovy对象上添加属性,你应该明确地设置一个ExpandoMetaClass:

class Item { 

    def Item() { 
     def mc = new ExpandoMetaClass(Item, false, true) 
     mc.initialize() 
     this.metaClass = mc 
    } 

    def propertyMissing(String name, value) { 
     this.metaClass."$name" = value 
    } 
} 
1

您遇到的一个问题是您正尝试向类MetaClass而不是实例MetaClass添加属性。并且因为您在创建实例后添加了属性,实例看不到它。例如,下面的代码无法打印属性:

class A { } 

def a = new A() 

A.metaClass.prop = 'value' 

println a.prop 

的错误是相当有趣:groovy.lang.MissingPropertyException: No such property: prop for class: A Possible solutions: prop

但是,即使你改变代码使用的实例MetaClass它仍然不能正常工作:

class Item { 
    def propertyMissing(String name, value) { 
     metaClass."$name" = value 
    } 
} 

def i1 = new Item() 

i1.prop = 'value' 
assert i1.prop == 'value' 

误差提供了一个线索:

groovy.lang.MissingPropertyException: No such property: prop for class: groovy.lang.MetaClassImpl 

提供类似Map功能的MetaClassExpandoMetaClass

instance.metaClass.prop = 'value' 

这样一个事实,即MetaClass不是ExpandoMetaClass意味着更换过程中没有发生:对象,直到你做这样的事情通常不会有这类MetaClass。很可能propertyMissing()在MOP过程中被调用得太晚以便以这种方式使用MetaClass

您提到过要使用Map动态属性添加属性而不是。然而,ExpandoMetaClass,这是你试图间接使用,使用...

... Map s的动态属性!你可以看到它here

实现你要找的是延长Expando行为最简单的方法:

class Item extends Expando { 
    def anotherProperty = 'Hello' 
} 

def i1 = new Item() 

i1.prop = 'value' 
assert i1.prop == 'value' 
assert i1.anotherProperty == 'Hello' 

Expando做所有的工作适合你。如果你想看看它是如何工作的,请阅读this

+0

哦,我明白了。我完全忽略了那个异常有MetaClassImpl。我刚刚看到MissingPropertyException,并没有看得更远...... – lapots