Groovy 闭包中的懒惰 GString 评估



我试图理解为什么在下面的代码片段中,如果在闭包内创建 GString 可以很好地评估它,但如果我尝试在闭包中创建字符串并尝试在闭包内评估它,则会引发异常:

map1 = ['foo': 1, 'bar': 2]
map2 = ['foo': 3, 'bar': 4]
dynamicallyGeneratedString = "key1: ${->key1}, val1: ${->value1}, key2: ${->key2}, val2: ${->value2}"
map1.each { key1, value1 ->
    map2.each { key2, value2 ->
        println "key1: ${->key1}, val1: ${->value1}, key2: ${->key2}, val2: ${->value2}" // works as expected
        // println dynamicallyGeneratedString // throws MissingPropertyException
    }
}

这两种情况下所需的输出都是:

key1: foo, val1: 1, key2: foo, val2: 3
key1: foo, val1: 1, key2: bar, val2: 4
key1: bar, val1: 2, key2: foo, val2: 3
key1: bar, val1: 2, key2: bar, val2: 4

我的目标是根据其他一些条件动态生成一个字符串,然后在循环浏览地图时懒惰地评估其内容。

这是一种有效的方法吗?

除了按照@Vampire的建议使用模板之外,我还可以想到两种解决任务的替代方法。

  • 在闭包中重新分配变量:

    map1 = ['foo': 1, 'bar': 2]
    map2 = ['foo': 3, 'bar': 4]
    def k1, v1, k2, v2
    dynamicString = "key1: ${->k1}, val1: ${->v1}, key2: ${->k2}, val2: ${->v2}"
    map1.each { key1, value1 ->
        map2.each { key2, value2 ->
            k1 = key1
            v1 = value1
            k2 = key2
            v2 = value2
            println dynamicString
        }
    }
    
  • 功能评估:

    map1 = ['foo': 1, 'bar': 2]
    map2 = ['foo': 3, 'bar': 4]
    def myfunc(key1, value1, key2, value2) {
        dynamicallyGeneratedString = "key1: ${key1}, val1: ${value1}, key2: ${key2}, val2: ${value2}"
    }
    map1.each { key1, value1 ->
        map2.each { key2, value2 ->
            println myfunc(key1, value1, key2, value2)
        }
    }
    

我想这只是一个品味问题...(或者我缺少任何性能注意事项?

问题是,当你创建GString时,它会存储对变量的引用。然后,当您尝试评估它时,这些引用不指向任何内容,并且您得到异常。

如果你真的想这样做,我认为你必须使用模板引擎,比如

println new groovy.text.GStringTemplateEngine().createTemplate(dynamicallyGeneratedString).make(key1: key1, value1: value1, key2: key2, value2: value2)

派对有点晚了,但在这里。

写了下面的类,我用它来动态构建SQL查询而不牺牲安全性,因为fill的结果是一个GStringImpl实例,并且groovy.sql.SQL正确地将其转换为参数化数据库查询。

我不确定 Closure 的调用方法是否是线程安全的(因为我正在设置委托属性(,所以我将同步添加到填充方法。

class GTemplate {
    def compiledTemplate
    GTemplate(String templateSource) {
        compiledTemplate = new GroovyShell().evaluate('{-> """' + escape(templateSource) + '""" }')
    }
    def static GTemplate compile(String templateSource) {
        return new GTemplate(templateSource)
    }
    def synchronized fill(def args) {
        compiledTemplate.delegate = args
        return compiledTemplate.call()
    }
    def synchronized fill(Map<?,?> args) {
        compiledTemplate.delegate = args
        return compiledTemplate.call()
    }
    private static String escape(String str) {
        StringBuilder buf = new StringBuilder()
        for(char c : str) {
            if ((c == '"') || (c == '\'))
                buf.append('\')
            buf.append(c)
        }
        return buf.toString()
    }
}
map1 = ['foo': 1, 'bar': 2]
map2 = ['foo': 3, 'bar': 4]
dynamicallyGeneratedString = GTemplate.compile('key1: ${->key1}, val1: ${->value1}, key2: ${->key2}, val2: ${->value2}')
map1.each { key1, value1 ->
    map2.each { key2, value2 ->
        println dynamicallyGeneratedString.fill(key1: key1, value1: value1, key2: key2, value2: value2)
    }
}

相关内容

  • 没有找到相关文章