Kotlin DSL с необязательными полями - PullRequest
1 голос
/ 05 мая 2020

В настоящее время я изучаю Kotlin DSL.

Я играл с ним некоторое время, но не могу решить свой вариант использования. У меня простой DSL, и меня не особо интересуют его типы, если я могу добиться такого синтаксиса:

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

В фоновом режиме я теперь хочу различать guish между определенными значениями, установленными в блоке ITEM. B легко, это просто значение, но становится сложно для A и C. Почему-то я не могу различить набор null и no 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)
}

Когда у меня есть последний объект, я хочу иметь возможность указать 4 разных этапа для каждого поля под ПУНКТ:

  • набор значений
  • нулевой набор
  • значение не установлено
  • поле пропущено

Я пробовал разные типы, но не повезло, так как большинство вещей влияет на синтаксис. Я также попытался изменить геттеры / сеттеры в построителе, чтобы, возможно, уловить там обновление и иметь дополнительное внутреннее свойство, которое обновляется, но ни get, ни set не вызываются для значения null / no. Также пытался изменить поля на функции, но тогда у меня есть уродливая скобка () в синтаксисе DSL.

Было бы здорово, если бы кто-нибудь мог помочь мне разобраться в этом.

Заранее спасибо!

1 Ответ

1 голос
/ 05 мая 2020

Для этого вы можете использовать приемники. Вот пример с одним параметром (в данном случае поле)

//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))

Вы можете сделать это с помощью дополнительных полей типа String? и переопределить их установщики, чтобы изменить фактические поля типа ItemValue. Я включил свойство hasBeenSet в ItemValue, чтобы показать, было ли оно установлено.

Чтобы пометить свойства как включенные без их установки, вы можете переопределить геттеры, чтобы они изменяли фактические поля, чтобы сделать их 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 . Вы можете запустить его и увидеть результат самостоятельно.

...