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

Kotlin中无需重写所有属性的建造者模式替代方案有哪些?

Kotlin 无需重写属性的封装式对象构建/更新方案

你的代码核心问题是:private set修饰的属性无法在类外部的lambda中直接赋值,因为lambda的执行作用域不属于UserEntity内部。以下是几种无需手动重写所有属性的替代方案:

方案1:利用Kotlin反射实现通用配置

通过反射可以在配置lambda中修改private属性,无需为每个属性编写单独的set方法。需要先添加kotlin-reflect依赖到项目中。

修改后的代码:

import java.time.OffsetDateTime
import java.time.OffsetDateTime.now
import java.util.UUID
import java.util.UUID.randomUUID
import kotlin.reflect.KMutableProperty
import kotlin.reflect.full.memberProperties

data class UserEntity(
    val id: UUID = randomUUID(),
) {
    var firstName: String = ""
        private set
    var lastName: String = ""
        private set
    var mainEmail: String = ""
        private set
    var isActive: Boolean = true
        private set
    var createdAt: OffsetDateTime = now()
        private set

    companion object {
        fun new(id: UUID = randomUUID(), block: UserEntity.() -> Unit): UserEntity {
            return UserEntity(id).apply {
                val user = this
                block(object : UserEntity() {
                    override fun setPropertyValue(name: String, value: Any?) {
                        user::class.memberProperties
                            .filterIsInstance<KMutableProperty<*>>()
                            .firstOrNull { it.name == name }
                            ?.let { prop ->
                                prop.isAccessible = true
                                @Suppress("UNCHECKED_CAST")
                                (prop as KMutableProperty<Any?>).set(user, value)
                            } ?: error("Property $name not found in UserEntity")
                    }
                })
            }.also { it.validate() }
        }
    }

    fun update(block: UserEntity.() -> Unit) {
        val user = this
        block(object : UserEntity() {
            override fun setPropertyValue(name: String, value: Any?) {
                user::class.memberProperties
                    .filterIsInstance<KMutableProperty<*>>()
                    .firstOrNull { it.name == name }
                    ?.let { prop ->
                        prop.isAccessible = true
                        @Suppress("UNCHECKED_CAST")
                        (prop as KMutableProperty<Any?>).set(user, value)
                    } ?: error("Property $name not found in UserEntity")
            }
        })
        validate()
    }

    protected open fun setPropertyValue(name: String, value: Any?) {}

    private fun validate(){
        require(firstName.isNotBlank()) { "First name cannot be blank" }
        require(mainEmail.isNotBlank()) { "Main email cannot be blank" }
    }
}

// 使用方式不变
val userEntity = UserEntity.new {
    firstName = "User"
    lastName = "Last"
    mainEmail = "user.last@email.com"
}

userEntity.update {
    firstName = "Qwerty"
    lastName = "Another"
    mainEmail = "qwerty.another@email.com"
}

该方案通过继承UserEntity的匿名代理类,结合反射机制实现属性修改,无需手动编写每个属性的set方法,保持配置lambda的简洁性。

方案2:使用internal setter限制修改范围

如果你的项目是单模块结构,可将属性setter改为internal,这样模块内部可以自由修改属性,外部模块无法直接赋值,同时保留简洁的lambda配置语法:

import java.time.OffsetDateTime
import java.time.OffsetDateTime.now
import java.util.UUID
import java.util.UUID.randomUUID

data class UserEntity(
    val id: UUID = randomUUID(),
) {
    var firstName: String = ""
        internal set
    var lastName: String = ""
        internal set
    var mainEmail: String = ""
        internal set
    var isActive: Boolean = true
        internal set
    var createdAt: OffsetDateTime = now()
        internal set

    companion object {
        fun new(id: UUID = randomUUID(), block: UserEntity.() -> Unit): UserEntity {
            return UserEntity(id).apply(block).also { it.validate() }
        }
    }

    fun update(block: UserEntity.() -> Unit) {
        this.apply(block).also { it.validate() }
    }

    private fun validate(){
        require(firstName.isNotBlank()) { "First name cannot be blank" }
        require(mainEmail.isNotBlank()) { "Main email cannot be blank" }
    }
}

// 使用方式完全不变
val userEntity = UserEntity.new {
    firstName = "User"
    lastName = "Last"
    mainEmail = "user.last@email.com"
}

userEntity.update {
    firstName = "Qwerty"
    lastName = "Another"
    mainEmail = "qwerty.another@email.com"
}

这是最简单的方案,利用Kotlin的internal可见性修饰符实现模块内封装,适合单模块项目,无需额外代码。

方案3:DSL作用域类(无反射)

如果不想依赖反射,也可以创建一个内部DSL作用域类,通过重载赋值操作符来映射属性修改,虽然需要列举一次属性,但比手动写Builder类更简洁:

import java.time.OffsetDateTime
import java.time.OffsetDateTime.now
import java.util.UUID
import java.util.UUID.randomUUID
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty

data class UserEntity(
    val id: UUID = randomUUID(),
) {
    var firstName: String = ""
        private set
    var lastName: String = ""
        private set
    var mainEmail: String = ""
        private set
    var isActive: Boolean = true
        private set
    var createdAt: OffsetDateTime = now()
        private set

    inner class ConfigScope {
        operator fun String.setValue(thisRef: Any?, property: KProperty<*>, value: String) {
            when (property.name) {
                "firstName" -> this@UserEntity.firstName = value
                "lastName" -> this@UserEntity.lastName = value
                "mainEmail" -> this@UserEntity.mainEmail = value
            }
        }

        operator fun Boolean.setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) {
            if (property.name == "isActive") {
                this@UserEntity.isActive = value
            }
        }

        operator fun OffsetDateTime.setValue(thisRef: Any?, property: KProperty<*>, value: OffsetDateTime) {
            if (property.name == "createdAt") {
                this@UserEntity.createdAt = value
            }
        }
    }

    companion object {
        fun new(id: UUID = randomUUID(), block: ConfigScope.() -> Unit): UserEntity {
            val user = UserEntity(id)
            user.ConfigScope().apply(block)
            user.validate()
            return user
        }
    }

    fun update(block: ConfigScope.() -> Unit) {
        ConfigScope().apply(block)
        validate()
    }

    private fun validate(){
        require(firstName.isNotBlank()) { "First name cannot be blank" }
        require(mainEmail.isNotBlank()) { "Main email cannot be blank" }
    }
}

// 使用方式不变
val userEntity = UserEntity.new {
    firstName = "User"
    lastName = "Last"
    mainEmail = "user.last@email.com"
}

userEntity.update {
    firstName = "Qwerty"
    lastName = "Another"
    mainEmail = "qwerty.another@email.com"
}

该方案通过内部类ConfigScope重载不同类型的赋值操作符,将lambda中的赋值映射到原对象的private属性,无需重写所有属性,且不依赖反射。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 12:18:21