2016-03-07 3 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 

Кроме того, если я достигаю metaClass, прежде чем пытаться установить 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 

Почему это имеет такое поведение?

ответ

2

Когда вы динамически обновляете метакласс объекта, Groovy заменяет метакласс на ExpandoMetaClass. Это специальная реализация MetaClass, которая поддерживает добавление и удаление свойств/методов.

Однако в вашем примере Item является GroovyObject, которые имеют постоянное поле на MetaClass. это поле не обновляется при обмене MetaClass: только метакласс в реестре заменяется ExpandoMetaClass. Этот вид кода может работать с javaobject, потому что у этого объекта нет поля, а класс соответствия -> метакласс выполняется каждый раз, когда он получает доступ к метаклассу.

В вы знаете, что вы собираетесь добавить свойства на заводной объекта, вы должны явно установить 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 

MetaClass, который обеспечивает функциональность Map, составляет ExpandoMetaClass. Объекты обычно не получают этот тип MetaClass, пока вы делаете что-то вроде этого:

instance.metaClass.prop = 'value' 

Поэтому тот факт, что MetaClass не является ExpandoMetaClass означает, что процесс замены не происходит. Вероятно, propertyMissing() получает слишком поздно в процессе СС, чтобы использовать 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

Смежные вопросы