Vykonávacie pozorovateľné vlastnosti, ktoré môžu tiež zoradenie v Kotlin

0

Otázka

Snažím sa stavať triedy, kde určité hodnoty sú Pozorovateľné, ale aj Serializable.

To samozrejme funguje a mód funguje, ale je to veľmi štandardný-ťažké museli pridať setter pre každé pole a manuálne toho, aby museli zavolať change(...) vo vnútri každej setter:

interface Observable {

    fun change(message: String) {
        println("changing $message")
    }
}

@Serializable
class BlahVO : Observable {

    var value2: String = ""
        set(value) {
            field = value
            change("value2")
        }

    fun toJson(): String {
        return Json.encodeToString(serializer(), this)
    }
}

println(BlahVO().apply { value2 = "test2" }) správne výstupy

changing value2
{"value2":"test2"}

Ja som sa snažil zaviesť Delegátov:

interface Observable {

    fun change(message: String) {
        println("changing $message")
    }

    
    @Suppress("ClassName")
    class default<T>(defaultValue: T) {

        private var value: T = defaultValue

        operator fun getValue(observable: Observable, property: KProperty<*>): T {
            return value
        }

        operator fun setValue(observable: Observable, property: KProperty<*>, value: T) {
            this.value = value
            observable.change(property.name)
        }

    }

}

@Serializable
class BlahVO : Observable {

    var value1: String by Observable.default("value1")

    fun toJson(): String {
        return Json.encodeToString(serializer(), this)
    }
}

println(BlahVO().apply { value1 = "test1" }) správne spúšťa zisťovania zmeny, ale to nie je zoradenie:

changing value1
{}

Ak idem z Pozorovateľných na ReadWriteProperty,

interface Observable {

    fun change(message: String) {
        println("changing $message")
    }

    fun <T> look(defaultValue: T): ReadWriteProperty<Observable, T> {
        return OP(defaultValue, this)
    }

    class OP<T>(defaultValue: T, val observable: Observable) : ObservableProperty<T>(defaultValue) {
        override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
            super.setValue(thisRef, property, value)
            observable.change("blah!")
        }
    }
}

@Serializable
class BlahVO : Observable {

    var value3: String by this.look("value3")

    fun toJson(): String {
        return Json.encodeToString(serializer(), this)
    }
}

výsledok je rovnaký:

changing blah!
{}

Podobne pre Delegátov.vetoable

var value4: String by Delegates.vetoable("value4", {
        property: KProperty<*>, oldstring: String, newString: String ->
    this.change(property.name)
    true
})

výstupy:

changing value4
{}

Delegáti len nezdá sa, že práca s Kotlin Serializácia

Aké ďalšie možnosti existujú, aby dodržiavali nehnuteľnosti zmien bez porušenia jej mód, že bude fungovať aj na iných platformách (KotlinJS, KotlinJVM, Android, ...)?

1

Najlepšiu odpoveď

2

Serializácia a Deserialization z Kotlin Delegátov nie je podporovaný kotlinx.serialization ako teraz.
Tam je otvorený problém #1578 na GitHub o túto funkciu.

Podľa problém, môžete vytvoriť vložených dát-prenos objekt, ktorý sa dostane na pokračovanie namiesto pôvodného objektu. Tiež by ste mohli napísať vlastné serializer na podporu mód z Kotlin Delegátov, ktoré sa zdá byť ešte viac štandardný, potom písať vlastné getry a tvorcami, ako je navrhnuté v otázke.


Prenos Dát Objektu

Podľa mapovanie svoj pôvodný objekt na jednoduchý prenos dát objekt bez delegátov, môžete využiť štandardný mód mechanizmy. To tiež má pekný vedľajší efekt očistiť svoje údaje model tried z rámca konkrétnych anotácie, ako @Serializable.

class DataModel {
    var observedProperty: String by Delegates.observable("initial") { property, before, after ->
        println("""Hey, I changed "${property.name}" from "$before" to "$after"!""")
    }

    fun toJson(): String {
        return Json.encodeToString(serializer(), this.toDto())
    }
}

fun DataModel.toDto() = DataTransferObject(observedProperty)

@Serializable
class DataTransferObject(val observedProperty: String)

fun main() {
    val data = DataModel()
    println(data.toJson())
    data.observedProperty = "changed"
    println(data.toJson())
}

To prináša nasledujúci výsledok:

{"observedProperty":"initial"}
Hey, I changed "observedProperty" from "initial" to "changed"!
{"observedProperty":"changed"}

Vlastné typ údajov

Ak zmena typu údajov je možnosť, mohli by ste napísať balenie trieda, ktorá dosahuje (de)na pokračovanie transparentne. Niečo v duchu takto by mohlo fungovať.

@Serializable
class ClassWithMonitoredString(val monitoredProperty: MonitoredString) {
    fun toJson(): String {
        return Json.encodeToString(serializer(), this)
    }
}

fun main() {
    val monitoredString = obs("obsDefault") { before, after ->
        println("""I changed from "$before" to "$after"!""")
    }
    
    val data = ClassWithMonitoredString(monitoredString)
    println(data.toJson())
    data.monitoredProperty.value = "obsChanged"
    println(data.toJson())
}

Čo dáva nasledujúci výsledok:

{"monitoredProperty":"obsDefault"}
I changed from "obsDefault" to "obsChanged"!
{"monitoredProperty":"obsChanged"}

Vy však stratiť informácie o majetku, ktoré zmenili, ako nebudete mať ľahký prístup na názov poľa. Tiež budete musieť zmeniť svoje štruktúry údajov, ako je uvedené vyššie a nemusí byť žiaduce, alebo dokonca možné. Okrem toho, táto práca len pre Reťazce teraz, aj keď človek môže robiť to viac všeobecný, aj keď. Tiež, to si vyžaduje veľa štandardný začať s. Na hovor stránky však stačí zabaliť skutočná hodnota hovoru obs. Použil som tieto štandardný dostať do práce.

typealias OnChange = (before: String, after: String) -> Unit

@Serializable(with = MonitoredStringSerializer::class)
class MonitoredString(initialValue: String, var onChange: OnChange?) {
    var value: String = initialValue
        set(value) {
            onChange?.invoke(field, value)

            field = value
        }

}

fun obs(value: String, onChange: OnChange? = null) = MonitoredString(value, onChange)

object MonitoredStringSerializer : KSerializer<MonitoredString> {
    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("MonitoredString", PrimitiveKind.STRING)

    override fun serialize(encoder: Encoder, value: MonitoredString) {
        encoder.encodeString(value.value)
    }

    override fun deserialize(decoder: Decoder): MonitoredString {
        return MonitoredString(decoder.decodeString(), null)
    }
}
2021-11-24 18:19:41

I v súčasnosti podľa podobný prístup, ale pripadá mi to ako by to mohlo byť lepšie. Som postúpila o krok ďalej vytvorenie metóda monitoredString, ktorý vracia MonitoredString a keďže funkcia má prístup k tejto, I don ' t musieť prejsť onChange, môžem len odkaz na to, aby OnChange z tohto. Nevýhodou s Pozorovateľných "štát", triedu a potom prenos dát triedy, ktorá môže byť na pokračovanie je duplicite model polia. Zdá sa, že len dobré riešenie, že dosiahne to, čo chcem urobiť, je komentár s @Niečo a potom vytvoriť štandardný pomocou KSP.
Jan Vladimir Mostert

V iných jazykoch

Táto stránka je v iných jazykoch

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................