HashMap的子类上的属性访问

时间:2016-08-12 19:29:30

标签: groovy

我有以下类是HashMap的子类,并且在地图中有一些条目的getter和setter。

请注意:此类来自我使用的框架,因此无法对其进行修改

class Mapish extends HashMap {
  static final PROPERTY_KEY = 'PROP'

  def getProperty() {
    get(PROPERTY_KEY)
  }

  def setProperty(def value) {
    put(PROPERTY_KEY, value)
  }
}

是否可以为此类关闭Groovy的Map属性表示法,以便属性访问将调用getter?换句话说,以下将通过?

def m = new Mapish()
m.setProperty('value')

assert m.property == 'value' // same as m.getProperty()

以下将抛出groovy.lang.MissingPropertyException

m.PROP

3 个答案:

答案 0 :(得分:1)

我相信meta-programming可以提供帮助。

请考虑以下事项:

// original

class Mapish extends HashMap {
    static final PROPERTY_KEY = 'PROP'

    def getProperty() {
        get(PROPERTY_KEY)
    }

    def setProperty(def value) {
        put(PROPERTY_KEY, value)
    }
}

// meta-programming augmentation

Mapish.metaClass.getProperty { String arg ->
    if (arg == "property") {
        delegate.getProperty()
    } else {
        throw new MissingPropertyException("illegal property: " + arg)
    }
}

// test

def m = new Mapish()

m.setProperty("foo")
assert "foo" == m.property

try {
    m.PROP
    throw IllegalStateException("should not get here")
} catch (MissingPropertyException ex) {
    // println "caught exception as expected"
}

答案 1 :(得分:0)

我不知道如何让地图在Groovy中不像地图一样。也许您应该考虑不要延长HashMap。这样的事情会给你你想要的行为:

class Mapish {
  static final PROPERTY_KEY = 'PROP'
  private map = [:]
  private getMap() { throw new groovy.lang.MissingPropertyException() }
  private void setMap(def map) { throw new groovy.lang.MissingPropertyException() }

  def getProperty() {
    map[PROPERTY_KEY]
  }

  def setProperty(def value) {
    map[PROPERTY_KEY] = value
  }
}

然后我希望您的代码示例完全按照您的意愿运行。

答案 2 :(得分:0)

经过长时间的调试后,我发现MetaClassImpl有一个名为isMap的属性。如果给定的类可分配给Map,则为true。这个变量使得类具有groovy Map语法糖,因此如果设置为false,则类实例将不会像Maps一样。唯一的问题是该字段为final,因此需要进行反射以将其设置为false

import java.lang.reflect.Field
import java.lang.reflect.Modifier

class Mapish extends HashMap {
  static final PROPERTY_KEY = 'PROP'

  def getProperty() {
    get(PROPERTY_KEY)
  }

  def setProperty(def value) {
    put(PROPERTY_KEY, value)
  }

}

// this method does basically this: c.metaClass.delegate.@isMap = false
// but since isMap is final the assignment throws exception
def unmapify(Class c) {
  def meta = c.metaClass.delegate
  def isMapField = meta.class.getDeclaredField("isMap")
  def modifiersField = Field.getDeclaredField("modifiers")
  modifiersField.accessible = true
  modifiersField.setInt(isMapField, isMapField.getModifiers() & ~Modifier.FINAL)

  isMapField.accessible = true
  isMapField.set(meta, false)
}

unmapify(Mapish)

def m = new Mapish()
m.setProperty('value')

assert m.property == 'value'

try {
  m.PROP
  assert false
} catch (ignore) {}