Kotlin中如何避免函数参数与数据类属性冗余传递?
Kotlin中避免重复定义数据类参数的维护方案
问题场景
现有一个表示用户设置更新请求的数据类:
@Serializable data class UpdateSettingsRequest( val isNotificationsOn: Boolean?, val isDarkMode: Boolean?, val language: String?, val isAnimationsEnabled: Boolean?, )
仓库类原本通过接收该数据类实例完成更新:
class SettingsRepository { fun updateSettings(request: UpdateSettingsRequest) { makeNetworkRequest(request) } private fun makeNetworkRequest(request: UpdateSettingsRequest) { TODO("make network request to update settings. request = $request") } }
为简化调用,曾将updateSettings改为接收独立参数,但带来维护问题:每次给UpdateSettingsRequest添加新字段,都需要同步修改updateSettings的参数列表,容易出错。
可行解决方案
方案1:使用可变构建器类
定义与数据类对应的可变构建器,通过lambda配置需要更新的字段,最后转换为不可变的请求实例。这种方式类型安全,维护成本较低:
// 可变构建器类,与UpdateSettingsRequest属性一一对应 class UpdateSettingsRequestBuilder { var isNotificationsOn: Boolean? = null var isDarkMode: Boolean? = null var language: String? = null var isAnimationsEnabled: Boolean? = null // 转换为不可变的请求数据类 fun build(): UpdateSettingsRequest = UpdateSettingsRequest( isNotificationsOn = isNotificationsOn, isDarkMode = isDarkMode, language = language, isAnimationsEnabled = isAnimationsEnabled ) } // 仓库类修改后的方法 class SettingsRepository { fun updateSettings(block: UpdateSettingsRequestBuilder.() -> Unit) { val request = UpdateSettingsRequestBuilder().apply(block).build() makeNetworkRequest(request) } private fun makeNetworkRequest(request: UpdateSettingsRequest) { TODO("make network request to update settings. request = $request") } }
调用方式简洁直观:
fun main() { val repo = getSettingsRepository() repo.updateSettings { isNotificationsOn = false } }
维护说明:当UpdateSettingsRequest添加新字段时,只需在UpdateSettingsRequestBuilder中新增对应可变属性,并在build()方法中同步赋值即可,IDE通常会给出明确提示,比同步修改函数参数更不容易遗漏。
方案2:给数据类添加默认值,结合命名参数调用
修改数据类,给所有参数添加默认值,仓库类仍接收数据类实例,但调用时直接通过命名参数构造实例,无需单独声明变量:
// 修改数据类,添加默认值 @Serializable data class UpdateSettingsRequest( val isNotificationsOn: Boolean? = null, val isDarkMode: Boolean? = null, val language: String? = null, val isAnimationsEnabled: Boolean? = null, ) // 仓库类保持原有方法 class SettingsRepository { fun updateSettings(request: UpdateSettingsRequest) { makeNetworkRequest(request) } private fun makeNetworkRequest(request: UpdateSettingsRequest) { TODO("make network request to update settings. request = $request") } }
调用方式:
fun main() { val repo = getSettingsRepository() // 直接通过命名参数构造实例,无需在外部单独声明变量 repo.updateSettings(UpdateSettingsRequest(isNotificationsOn = false)) }
维护说明:当数据类添加新字段时,只需给新字段设置默认值,无需修改仓库类的任何代码,调用方也无需调整(除非需要使用新字段)。这种方式最简洁,完全避免了重复定义参数的问题。
方案3:利用反射自动构建(不推荐)
通过反射自动匹配数据类的构造参数,无需手动同步字段,但存在类型不安全、性能开销的问题,仅适合非核心场景:
class SettingsRepository { fun updateSettings(vararg updates: Pair<String, Any?>) { val updateMap = updates.toMap() // 获取数据类的主构造函数 val constructor = UpdateSettingsRequest::class.constructors.first() // 按构造参数顺序匹配值,未指定的字段为null val args = constructor.parameters.map { param -> updateMap[param.name] } val request = constructor.call(*args.toTypedArray()) makeNetworkRequest(request) } private fun makeNetworkRequest(request: UpdateSettingsRequest) { TODO("make network request to update settings. request = $request") } }
调用方式:
fun main() { val repo = getSettingsRepository() repo.updateSettings("isNotificationsOn" to false) }
注意:这种方式依赖字段名字符串,容易因拼写错误导致问题,且反射会影响性能,不建议在生产环境使用。
内容的提问来源于stack exchange,提问作者Saman Sattari
相关产品推荐
相关产品推荐

