【问题标题】:Dynamically evaluating templated Strings in KotlinKotlin 中动态评估模板字符串
【发布时间】:2017-05-16 19:00:33
【问题描述】:

假设我有以下 Kotlin 代码:

fun main(args: Array<String>) {
    val a = "test"
    println(args.first())
}

如果我传入一个参数$a,输出将是$a。据我了解,Kotlin 通过在编译时生成输出代码来处理字符串模板,大概是使用 StringBuilder。是否有某种方法可以针对当前上下文中的模板评估不在源代码中的字符串?字符串模板非常有用,能够评估来自动态上下文(例如配置文件)的表达式会很棒,但据我所知,这是做不到的。

缺乏这一点,什么是解决这个问题的好方法?调用脚本引擎?

【问题讨论】:

    标签: templates dynamic kotlin


    【解决方案1】:

    如果您需要以这种方式计算任意表达式,那么是的,您需要一个脚本引擎。 Kotlin 有一个可以使用的JSR 223 实现,请参阅示例here (the kotlin-jsr223-* projects)

    这是一个基本的用法示例:

    val engine = ScriptEngineManager().getEngineByExtension("kts")!!
    engine.eval("val x = 3")
    val res = engine.eval("x + 2")
    Assert.assertEquals(5, res)
    

    代码取自KotlinJsr223ScriptEngineIT.kt,记得configure the service via META-INF

    【讨论】:

    • Kotlin 本身的脚本引擎听起来很完美。当操作的参数是 Kotlin 时,我想避免将 JavaScript 或 JRuby 引入其中。
    • 顺便说一下,我自己也试过了,发现a problem是这个工具的1.1.2版本,所以如果你也遇到了,那么我推荐使用1.1。 1 直到它被修复。
    • 这里是 1.1.2,但经过一些实验后,我发现要访问未在脚本本身中声明但通过绑定传递给脚本引擎的变量,您必须向我们构造 bindings["var_name"] as Type。这是故意的吗?会一直这样吗?在 Rhino 中,您可以执行类似 var_name + 2 的操作,然后它会进行评估。最后,我需要这个的要求可能需要编写一个解析器,但尝试起来很有趣。
    • @G_H,我认为您可以通过首先绑定一个值然后评估将其声明为脚本中的局部变量的代码行来解决这个问题。像这样的东西:(gist)
    【解决方案2】:

    为了完整起见,这里还有一些在 Kotlin 代码中动态评估字符串模板的方法。

    字符串模板的正常行为是在编译时评估字符串的内容。在你的代码执行期间它不会改变:

    fun main() {
        var subject = "world"
        val hello = "hello $subject"
    
        subject = "stackoverflow"
        println(hello)
    }
    
    // Output: hello world
    

    此限制有多种解决方法:

    Lambda 函数

    您可以将 String 包装在 lambda 函数中,因此每次调用函数时,它都会评估 String 的新实例。

    fun main() {
        var subject = "world"
        val hello = {"hello $subject"}
    
        subject = "stackoverflow"
        println(hello())
    }
    
    // Output: hello stackoverflow
    

    优势

    • 支持字符串操作

    缺点

    • 必须像函数一样调用它

    匿名对象

    您可以创建匿名对象(类似于 Java 中的静态)并覆盖 toString 方法。

    fun main() {
        var subject = "world"
        val hello = object { override fun toString() = "hello $subject"}
    
        subject = "stackoverflow"
        println(hello())
    }
        
    // Output: hello stackoverflow
    

    注意:我不推荐这种方法。

    优势

    • 无需函数调用

    缺点

    • 不支持所有字符串操作

    财产委托

    您可以将属性运算符函数委托给外部类,以实现字符串模板的动态评估。

    import kotlin.reflect.KProperty
    
    fun main() {
        var subject = "world"
        val hello: String by dynamicStringTemplate { "hello $subject" }
    
        subject = "stackoverflow"
        println(hello)
    }
    
    // Output: hello stackoverflow
     
    class dynamicStringTemplate(var value: () -> String) {
        operator fun getValue(thisRef: Nothing?, prop: KProperty<*>): String {       
            return this.value()
        }
     
        operator fun setValue(thisRef:  Nothing?, prop: KProperty<*>, value: String) {
            this.value = {value}
        }
    }
    

    优势

    • 支持所有字符串操作
    • 与字符串作用相同

    缺点

    • 必须创建其他类

    【讨论】:

      【解决方案3】:

      是否有某种方法可以针对当前上下文中的模板评估源代码中没有的字符串?

      您的代码示例中没有模板字符串,a 未使用。我是否正确理解您想做类似val evaluated = evalStringTemplate(template, arg1, arg2, ...) 之类的事情,而templateString,例如"$a",而arg1、arg2、...是模板的参数?

      如果是这样,则没有特定于 Kotlin 的方法来执行此操作,但您可以使用 Java Formatter 类。

      【讨论】:

      • 格式化字符串就是这样做的,它格式化。 Kotlin 模板可以包含表达式,例如算术运算、布尔逻辑、方法调用等。这就是我所追求的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-11-17
      • 1970-01-01
      • 2011-03-12
      • 2021-12-12
      • 2016-08-28
      • 1970-01-01
      • 2017-10-20
      相关资源
      最近更新 更多