You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Brigadier Kotlin DSL中by限定符仅在run/apply内可用的问题求助

解决Brigadier Kotlin DSL中by限定符只能在接收者作用域使用的问题

在集成Brigadier的Kotlin DSL库时,遇到一个限制:库提供的by语法只能在DslCommandBuilder<*>作为当前作用域this时才能使用。示例代码如下:

fun test(builder: DslCommandBuilder<ClientCommandSource>) {
    builder.apply {
        val bar by this.integer("bar") // 正常工作
    }
    val bar by builder.integer("bar") // 编译错误,无法使用by
}

我们希望在自定义Command的execute方法中,不用手动套builder.run{}就能直接使用DSL语法,比如:

object SetCommand : Command("set", "allows you to configure module settings") {
    override fun execute(builder: DslCommandBuilder<ClientCommandSource>) {
        // 希望在这里直接用 val bar by integer("bar")
    }
}

最优解决方案:修改父类Command的execute为带接收者的函数

最优雅的解决方式是调整父类Command的方法签名,让execute成为DslCommandBuilder<ClientCommandSource>的接收者函数,这样子类实现时,作用域内的this就是DslCommandBuilder实例,直接使用库的DSL语法即可。

步骤1:修改父类Command

abstract class Command(name: String, description: String) {
    // 将原来的execute方法改为带接收者的函数
    abstract fun DslCommandBuilder<ClientCommandSource>.execute()
}

步骤2:子类实现execute

此时子类重写execute时,自然处于DslCommandBuilder的作用域中,无需额外的run/apply:

object SetCommand : Command("set", "allows you to configure module settings") {
    override fun DslCommandBuilder<ClientCommandSource>.execute() {
        // 直接使用DSL语法,无需builder前缀或run块
        val bar by integer("bar")
        val foo by string("foo")
        
        // 后续命令逻辑
        runs {
            // 处理参数bar和foo
        }
    }
}

步骤3:触发execute方法

在需要构建命令的地方,通过run方法将builder作为接收者传入即可:

val commandBuilder = DslCommandBuilder<ClientCommandSource>()
commandBuilder.run { SetCommand.execute() }

替代方案(无法修改父类时)

如果因为项目限制无法修改父类Command的结构,可以封装一个扩展函数来简化调用:

// 给Command类添加扩展函数,自动将builder作为接收者传入
fun Command.executeWithReceiver(builder: DslCommandBuilder<ClientCommandSource>) {
    builder.run {
        execute(builder)
    }
}

// 使用方式
SetCommand.executeWithReceiver(commandBuilder)

不过这种方式还是需要在execute方法内部套run块,不如方案一优雅。

问题根源解释

库中的by语法依赖于Kotlin的委托属性和接收者扩展函数:integer()等方法是DslCommandBuilder的扩展函数,返回的ArgumentDelegate类型的provideDelegate方法需要当前作用域的this是DslCommandBuilder实例,才能正确绑定命令参数到builder。直接调用builder.integer()时,provideDelegate无法获取到正确的接收者上下文,因此无法使用by关键字。

内容的提问来源于stack exchange,提问作者OffeeX

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 06:52:41