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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 08:56:05