【问题标题】:How to use propertyMissing on a class that implements java.util.Map in groovy如何在groovy中实现java.util.Map的类上使用propertyMissing
【发布时间】:2015-05-16 12:54:58
【问题描述】:

我理解we cannot access Map properties the same way we access them in other classes,因为能够在 groovy 中获取带有点符号的映射键。

现在,对于实现 java.util.Map 的类,有没有办法仍然受益于使用 propertyMissing 的 expando 元类?

这是我正在尝试的:

LinkedHashMap.metaClass.methodMissing = { method, args ->
    println "Invoking ${method}"
    "Invoking ${method}"
}

LinkedHashMap.metaClass.propertyMissing = { method, args ->
    println "Accessing ${method}"
    "Accessing ${method}"
}

def foo = [:]

assert "Invoking bar" == foo.bar() // this works fine
assert "Accessing bar" == foo.bar  // this doesn't work, for obvious reasons, but I'd like to be able to do that...

我一直在尝试通过自定义 DelegatingMetaClasses 但没有成功...

【问题讨论】:

  • 你能举一个有用的例子吗?举个例子,也许我们可以想办法做到这一点
  • 访问地图中不存在的键时,我想尝试获取从缺少的键名派生的另一个键...
  • 例如,对于任何地图,如果键 foo_bar 不存在,我想查找 fooBar

标签: groovy


【解决方案1】:

不确定它是否适合您的用例,但您可以在地图上使用 Guava 和 withDefault 方法...

@Grab( 'com.google.guava:guava:16.0.1' )
import static com.google.common.base.CaseFormat.*

def map
map = [:].withDefault { key -> 
    LOWER_UNDERSCORE.to(LOWER_CAMEL, key).with { alternate ->
        map.containsKey(alternate) ? map[alternate] : null
    }
}

map.possibleSolution = 'maybe'

assert map.possible_solution == 'maybe'

这样做的一个副作用是,在断言之后,映射包含两个键:值对:

assert map == [possibleSolution:'maybe', possible_solution:'maybe']

【讨论】:

  • 看起来像一个解决方案...我会尝试的。谢谢!
【解决方案2】:

如果我理解得很好,您可以提供自定义地图:

class CustomMap extends LinkedHashMap {

    def getAt(name) {
        println "getAt($name)"
        def r = super.getAt(name)
        r ? r : this.propertyMissing(name)
    }

    def get(name) {
        println "get($name)"
        super.get(name)
        def r = super.get(name)
        r ? r : this.propertyMissing(name)

    }
    def methodMissing(method, args) {
        println "methodMissing($method, $args)"
        "Invoking ${method}"
    }

    def propertyMissing(method) {
        println "propertyMissing($method)"
        "Accessing ${method}"
    }
}

def foo = [bar:1] as CustomMap

assert foo.bar == 1
assert foo['bar'] ==  1
assert foo.lol == 'Accessing lol'
assert foo['lol'] ==  'Accessing lol'
assert foo.bar() == 'Invoking bar'

【讨论】:

  • 感谢 Opal,这会起作用,但这不是一个选择。我希望这适用于现有 API 返回的地图。
【解决方案3】:

我重读了groovy Maps javadocs,发现get 方法有两个版本。一个接受一个参数,一个接受 2 个。

采用 2 的版本几乎与我在此处描述的一样:如果找不到您的密钥,它会返回默认值。

我得到了想要的效果,但不是点表示法,因此我只是将其作为替代解决方案发布,以防有​​人遇到此帖子:

Map.metaClass.customGet = { key ->
    def alternate = key.replaceAll(/_\w/){ it[1].toUpperCase() }
    return delegate.get(key, delegate.get(alternate, 'Sorry...'))
}

def m = [myKey : 'Found your key']

assert 'Found your key' == m.customGet('myKey')

assert 'Found your key' == m.customGet('my_key')

assert 'Sorry...' == m.customGet('another_key')

println m

-结果-

m = [myKey:找到您的密钥,my_key:找到您的密钥,anotherKey:抱歉...,another_key:抱歉...]

在 Tim 的解决方案中,这会导致 m 在第二个断言之后包含两个键 + 2 个具有默认值的键(对不起...)每次我们要求初始映射中不存在的新值...这可能通过删除具有默认值的键来解决。例如:

Map.metaClass.customGet = { key ->
    def alternate = key.replaceAll(/_\w/){ it[1].toUpperCase() }
    def ret = delegate.get(key, delegate.get(alternate, 'Sorry...'))
    if (ret == 'Sorry...') {
        delegate.remove(key)
        delegate.remove(alternate)
    }
    ret
}

请随时评论/纠正这可能导致的任何错误......在这里大声思考......

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-01-26
    • 2016-03-19
    • 2010-10-26
    • 1970-01-01
    • 2015-04-11
    • 2015-03-07
    • 1970-01-01
    相关资源
    最近更新 更多