1

我目前正在学习 Kotlin DSL。

我已经玩了一段时间了,但我无法解决我的用例。我有一个简单的 DSL,只要我能实现这样的语法,我就不会太在意它的类型:

    private fun getObj(): SET {
        return SET {
            ITEM {
                A = null
                B = "Hello world"
                C
                // D - exists in DSL but omitted here
            }
        }
    }

在后台,我现在想区分ITEM块中设置的某些值。B很容易,它只是价值,但它变得很难AC。不知何故,我无法区分nullno value设置。目前我的构建器看起来像这样,但我愿意更改它以实现上述语法:

class ITEMBuilder {
    var A: String? = null
    var B: String? = null
    var C: String? = null
    var D: String? = null

    fun build() = ITEM(
        ItemValue(A),
        ItemValue(B),
        ItemValue(C),
        ItemValue(D)
    )
}

class ItemValue(val include: Boolean? = false, val value: String? = null) {
    constructor(value: String? = null): this(null != value, value)
}

当我有了最终对象时,我希望能够为 ITEM 下的每个字段区分 4 个不同的阶段:

  • 值集
  • 空集
  • 未设置值
  • 字段省略

我尝试了不同的类型,但没有运气,因为大多数事情都会影响语法。我还尝试更改构建器中的 getter/setter,以便在那里捕获更新并具有更新的附加内部属性 - 但既没有也没有get调用setnull/no 值。还尝试将字段更改为函数,但后来我()在 DSL 语法中有丑陋的括号。

如果有人可以帮助我解决这个问题,那就太好了。

提前致谢!

4

1 回答 1

1

您可以使用接收器来实现这一点。这是一个带有单个参数的示例(在本例中为字段)

//Use like this
val item = ITEM {
        A = "Yay"
        B //Not omitted, but also not set
        //C can be omitted
        D = null
    }

这相当于

Item(//Included and set to "Yay"
     a=ItemValue(include=true, hasBeenSet=true, value="Yay"), 
     //Included, but not yet set
     b=ItemValue(include=true, hasBeenSet=false, value=null), 
     //Not included, and so not yet set
     c=ItemValue(include=false, hasBeenSet=false, value=null), 
     //Included, and set to null (Same as A)
     d=ItemValue(include=true, hasBeenSet=true, value=null))

您可以借助额外的 type 字段来执行此操作,String?并覆盖它们的 setter 以修改 type 的实际字段ItemValue。我在其中包含了一个hasBeenSet属性ItemValue以显示它是否已设置。

要将属性标记为包含而不设置它们,您可以覆盖 getter,以便它们修改实际字段以使其成为ItemValue(true, false),这意味着它们被包含但未设置。

class Builder {
    var A: String? = null
        set(value) {
            a = ItemValue(true, true, value)
        }
        get() {
            a = ItemValue(true, false)
            return field
        }
    var B: String? = null
        set(value) {
            b = ItemValue(true, true, value)
        }
        get() {
            b = ItemValue(true, false)
            return field
        }
    var C: String? = null
        set(value) {
            c = ItemValue(true, true, value)
        }
        get() {
            c = ItemValue(true, false)
            return field
        }
    var D: String? = null
        set(value) {
            d = ItemValue(true, true, value)
        }
        get() {
            d = ItemValue(true, false)
            return field
        }

    var a: ItemValue = ItemValue(false, false)
    var b: ItemValue = ItemValue(false, false)
    var c: ItemValue = ItemValue(false, false)
    var d: ItemValue = ItemValue(false, false)

    fun build(): Item {
        return Item(a, b, c, d)
    }
}

fun ITEM(setters: Builder.() -> Unit): Item {
    val builder = Builder()
    builder.setters()
    return builder.build()
}

data class Item(val a: ItemValue, val b: ItemValue, val c: ItemValue, val d: ItemValue)
data class ItemValue(val include: Boolean, val hasBeenSet: Boolean, val value: String? = null)

这是 Kotlin Playground 的链接。您可以运行它并自己查看输出。

于 2020-05-05T19:04:13.167 回答