Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

161
Visualizações
How to update kotlin data class with properties of a different data class

If I have for example an entity:

   data class SampleEntity(
            val a: Int,
            val b: Int,
            val c: Int,
            val d: Int){
     fun update(form: SampleEntityUpdateForm): SampleEntity{
       ... 
       return this SampleEntity updated with form
     } 
}

and a form to update this entity:

data class SampleEntityUpdateForm(
        val a: Int?,
        val b: Int?,
        val c: Int?,
        val d: Int?
)

What is the best way to update the entity with this form (i.e. implementing SampleEntity.update(...)), leaving the properties that are null in SampleEntityUpdateForm as they were in the original entity, with minimum boiler plate.

So for example:

val sampleEntityInst = SampleEntity(1,2,3,4)

val sampleEntityUpdateForm(b = 20, d = 40)

val updatedSampleEntityInst = sampleEntityInst.update(sampleEntityUpdateForm)

where updatedSampleEntityInst will equal SampleEntity(1,20,3,40).

obviosly I could implement SampleEntity.update as:

fun update(form: SampleEntityUpdateForm) = SampleEntity(
        a = form.a ?: this.a,
        b = form.b ?: this.b,
        c = form.c ?: this.c,
        d = form.d ?: this.d,
)

But this has quite a bit of boiler plate/repetition. Is there something already made to allow doing this with less boiler plate/repetition?

over 4 years ago · Santiago Trujillo
2 Respostas
Responde à pergunta

0

Checkout the copy method available on all data classes, e.g:


data class SampleEntity(
    val a: Int,
    val b: Int,
    val c: Int,
    val d: Int){
    fun update(form: SampleEntityUpdateForm): SampleEntity{

        return  this.copy(a = form.a, b = form.b, ...)
    }
}

Edit: I did miss the nullability twist. Using the magic function below the following works but it in my opinion the straightforward approach has better ergonomics.

There may be use cases where reflection is OK: a high number of properties, a tricky transformation of the incoming parameters, etc...

fun main() {
    val sampleEntityForm = SampleEntityUpdateForm(a = null, b = 20, c = null, d = 40)
    val sampleEntityInst = SampleEntity(1, 2, 3, 4)

    magic(sampleEntityForm, sampleEntityInst).also { 
        println(it) 
    } // prints: SampleEntity(a=1, b=20, c=3, d=40)
}


fun magic(from: SampleEntityUpdateForm, using: SampleEntity): SampleEntity {
    val fromValues = from::class.members.filterIsInstance<KProperty1<SampleEntityUpdateForm, Int?>>().map { it.get(from) }
    val usingValues = using::class.members.filterIsInstance<KProperty1<SampleEntity, Int>>().map { it.get(using) }

    val nullsEliminated = usingValues.mapIndexed { i, value -> fromValues[i] ?: value }.toTypedArray()

    return SampleEntity::class.primaryConstructor!!.call(*nullsEliminated)
}


over 4 years ago · Santiago Trujillo Relatório

0

This is the problem with static typing. Boilerplate and repetitiveness come built-in. I think your example with a = form.a ?: this.a is a good solution. Using a statically typed language like Kotlin, it's better to accept the boilerplate and move on.

If you want to be more fancy, you have to use something more dynamic. For example, you can go via JSON, which is more flexible. This solution uses JSON-P's JsonMergePatch to create a patch and apply it to your object. I'm using Jackson to convert between POJOs and JSON objects via reflection.

This method has the benefit of being able to set values to null via the patch too. If the value in the patch is null, it sets the target value to null. If the value is not in the patch, the existing value is preserved. This is something you can't do nicely natively; you have to start going crazy with things like nullable Optionals.

fun main() {
    val original = SampleEntity(a = 0, b = 0, c = 0, d = 0)
    val patch = Json.createObjectBuilder()
        .add("b", 2)
        .add("d", 4)
        .build().let { Json.createMergePatch(it) }
    val patched: SampleEntity = original.update(patch)
    println(patched)
}

data class SampleEntity(
    val a: Int,
    val b: Int,
    val c: Int,
    val d: Int
) {
    fun update(patch: JsonMergePatch): SampleEntity {
        val jsonValue = objectMapper.convertValue<JsonValue>(this)
        val patched = patch.apply(jsonValue)
        return objectMapper.convertValue(patched)
    }

    companion object {
        private val objectMapper: ObjectMapper = ObjectMapper()
            .registerKotlinModule()
            .registerModule(JSONPModule())
    }
}

Output:

SampleEntity(a=0, b=2, c=0, d=4)

Dependencies:

dependencies {
    implementation("jakarta.json", "jakarta.json-api", "2.0.1")
    implementation("org.glassfish", "jakarta.json", "2.0.1")
    implementation("com.fasterxml.jackson.core", "jackson-core", "2.13.0")
    implementation("com.fasterxml.jackson.datatype", "jackson-datatype-jakarta-jsonp", "2.12.2")
    implementation("com.fasterxml.jackson.module", "jackson-module-kotlin", "2.13.0")
}
over 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda