从DSL块中的语句创建列表

时间:2018-03-06 13:14:22

标签: kotlin

在尝试构建用于验证的DSL时,我正在寻找用接收器收集块/ lambda中的语句的方法。为了说明,这里是一个没有实际验证逻辑的最小例子:

data class Constraint(val hint: String)

class Validation(val constraints: List<Constraint>) {

    companion object {
        operator fun invoke(init: (ValidationBuilder.() -> Unit)): Validation {
            return ValidationBuilder().apply(init).build()
        }
    }

    class ValidationBuilder {
        private var constraints: MutableList<Constraint> = mutableListOf()

        operator fun Constraint.unaryPlus() {
            constraints.add(this)
        }

        fun build() = Validation(constraints)
    }
}

然后可以使用它来构建像这样的验证

val validation = Validation {
    +Constraint("First constraint")
    val secondConstraintHint = "Second constraint"
    +Constraint(secondConstraintHint)
}

我想摆脱unaryPlus运算符并直接收集块中被评估为Constraint的单个语句,以便我可以执行以下操作:

val validation = Validation {
    Constraint("First constraint")
    val secondConstraintHint = "Second constraint"
    Constraint(secondConstraintHint)
}

这有可能吗?

为了给出更多的上下文,我瞄准的实际结果看起来更像是这样:

Validation<User> {
    User::firstName {
        val min = 2
        minLength(min) hint "Please provide a first name"
        maxLength(200) // uses default hint
    }
}

1 个答案:

答案 0 :(得分:1)

好吧,似乎没有直接的解决方案,因为Kotlin无法处理未在任何地方分配,返回或传递的评估表达式结果。

一种可能的解决方法是使用为构建器定义的函数来模拟所需的构造函数:

class ValidationBuilder {
    /* ... */

    fun Constraint(name: String) = 
        full.qualified.name.of.Constraint(name).also(constraints::add)
}

不幸的是,这将要求您以这种方式复制要调用的所有签名。

UPD(回复评论):我认为用户定制DSL的惯用方法是为DSL构建者定义自己的扩展:

fun ValidationBuilder.nonEmptyText(min: Int = 1, max: Int = 65.536) = TODO()

如果来自DSL外部的Constraint是一个重要的用例,您可以使用特殊功能(例如fun ValidationBuilder.constraint(...))覆盖它,并让用户将其扩展名委托给它。