我试着理解为什么在下面的代码片段中,如果在封闭内部创建了GString,那么它会被评估为正常,但如果我尝试在闭包内部创建String并尝试在闭包内部进行评估,则会抛出异常:
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
我的目标是根据其他一些条件动态生成一个String,然后在循环遍历地图时懒惰地评估其内容。
这是一种有效的方法吗?
答案 0 :(得分:1)
问题是,当您创建GString时,它会存储对变量的引用。当你尝试评估它时,那些引用指向什么都没有,你得到了例外。
如果你真的想这样做,我认为你必须使用像
这样的模板引擎println new groovy.text.GStringTemplateEngine().createTemplate(dynamicallyGeneratedString).make(key1: key1, value1: value1, key2: key2, value2: value2)
答案 1 :(得分:1)
除了使用@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)
}
}
我想这只是一个品味问题...(或者我是否缺少任何性能因素?)
答案 2 :(得分:1)
聚会晚了一点,但是在这里。
我编写了以下类,我将其用于动态构建SQL查询而不牺牲安全性,因为fill的结果是 GStringImpl 实例,并且 groovy.sql.Sql 正确地对其进行了转换进入参数化的数据库查询。
我不确定Closure的 call 方法是否是线程安全的(因为我正在设置 delegate 属性),所以我向其中添加了 synchronized 填充方法。
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)
}
}