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

如何使用另一个data class的属性更新Kotlin data class并减少样板代码

解决方案

以下是几种不同场景下的简化实现方案:

方案1:无反射原生写法(优先推荐)

你当前的写法本身是编译期安全、运行性能最高的实现,没有额外依赖,也不存在反射的兼容问题。如果实体字段数量不多(<20个),完全可以保留该逻辑,只需要利用data class自带的copy方法简化写法,减少冗余的this引用:

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

方案2:反射通用实现(适合字段多、实体多的场景)

如果你有大量类似的「实体+更新表单」的结构,不想每个实体都重复写赋值逻辑,可以封装通用反射工具方法,一次编写后所有对应结构的实体都可以复用:

import kotlin.reflect.full.memberProperties
import kotlin.reflect.full.primaryConstructor

// 通用扩展方法,所有data class都可调用
fun <T : Any, F : Any> T.updateWith(form: F): T {
    val constructor = this::class.primaryConstructor
        ?: throw IllegalArgumentException("${this::class.simpleName} 不存在主构造器")
    // 提取原实体所有属性的键值对
    val entityProps = this::class.memberProperties.associate { it.name to it.get(this) }
    // 提取表单所有非null属性的键值对
    val formProps = form::class.memberProperties
        .associate { it.name to it.get(form) }
        .filterValues { it != null }
    // 合并属性,表单非null值覆盖原实体值
    val mergedProps = entityProps + formProps
    // 按主构造器参数顺序拼装参数
    val args = constructor.parameters.map { mergedProps[it.name] }
    @Suppress("UNCHECKED_CAST")
    return constructor.call(*args.toTypedArray()) as T
}

之后你的SampleEntity的update方法可以简化为一行:

fun update(form: SampleEntityUpdateForm) = updateWith(form)

注意:该方案需要引入org.jetbrains.kotlin:kotlin-reflect依赖,运行时会有轻微性能损耗,适合非性能敏感的业务场景,同时要求表单类字段名和实体类字段名完全对应。


方案3:编译期注解生成(兼顾性能与无样板)

如果你既不想写样板代码,又不想承担反射的性能损耗,可以基于KSP(Kotlin符号处理工具)自定义注解,编译期自动为对应实体生成update方法的代码。该方案适合中大型项目存在大量同类更新场景的情况,需要提前开发对应的注解处理器。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 09:54:00