Kotlin构建Data Class时仅非空属性赋值的简洁实现方案
Kotlin数据类Builder模式避免重复默认值的简洁实现方式
问题描述
我定义了如下Kotlin数据类MyDataClass:
data class MyDataClass(val x: String, val y: String, val something: Something = Something(), val somethingElse : SomethingElse = SomethingElse())
由于该类需同时支持Kotlin和Java调用,我为其实现了Builder模式,但希望避免重复定义默认值——仅当Builder中设置的something、somethingElse属性非空时,才覆盖数据类的默认值。当前采用多分支判断的实现方式冗长且易出错,请问是否有更简洁、更符合Kotlin风格的实现方法?
解决方案
这个需求太常见了!Kotlin的空安全特性和?: Elvis操作符刚好能帮我们优雅解决这个问题,彻底摆脱繁琐的多分支判断。核心思路是:让Builder中的可选属性为可空类型,在构建数据类时,用?:直接复用数据类本身的默认值,只有当用户显式设置了属性值(非空)时才覆盖。
1. 实现Builder类
首先定义Builder,把可选属性设为可空,同时为Java调用者提供链式setter(Kotlin用户可以直接用属性赋值,Java用户用setter更顺手):
class MyDataClassBuilder { // 必传属性设为可空,后续在build时做非空校验 var x: String? = null var y: String? = null // 可选属性设为可空,默认不赋值(即null) var something: Something? = null var somethingElse: SomethingElse? = null // Java风格的链式setter fun setX(x: String): MyDataClassBuilder { this.x = x return this } fun setY(y: String): MyDataClassBuilder { this.y = y return this } fun setSomething(something: Something): MyDataClassBuilder { this.something = something return this } fun setSomethingElse(somethingElse: SomethingElse): MyDataClassBuilder { this.somethingElse = somethingElse return this } // 核心构建方法,用?:复用数据类默认值 fun build(): MyDataClass { return MyDataClass( // 必传参数必须非空,用requireNotNull做友好校验 x = requireNotNull(x) { "Parameter 'x' must not be null" }, y = requireNotNull(y) { "Parameter 'y' must not be null" }, // 仅当Builder中设置了非空值时才覆盖,否则用数据类默认值 something = something ?: Something(), somethingElse = somethingElse ?: SomethingElse() ) } }
2. 为数据类添加Builder入口
在MyDataClass的伴生对象中添加两种Builder入口:一种是Kotlin风格的lambda构建函数,另一种是Java风格的静态builder方法,兼顾两种语言的使用习惯:
data class MyDataClass( val x: String, val y: String, val something: Something = Something(), val somethingElse: SomethingElse = SomethingElse() ) { companion object { // Kotlin用户友好的lambda构建方式 fun build(block: MyDataClassBuilder.() -> Unit): MyDataClass { val builder = MyDataClassBuilder() builder.block() return builder.build() } // Java用户友好的静态builder方法 @JvmStatic fun builder(): MyDataClassBuilder { return MyDataClassBuilder() } } }
3. 使用示例
Kotlin调用方式(简洁的lambda风格)
// 不设置可选属性,直接用数据类默认值 val defaultData = MyDataClass.build { x = "Hello" y = "Kotlin" } // 仅设置其中一个可选属性,另一个用默认值 val customData = MyDataClass.build { x = "Hello" y = "Kotlin" something = Something("Custom Value") }
Java调用方式(链式调用风格)
// 用默认值构建 MyDataClass defaultData = MyDataClass.builder() .setX("Hello") .setY("Java") .build(); // 自定义可选属性 MyDataClass customData = MyDataClass.builder() .setX("Hello") .setY("Java") .setSomethingElse(new SomethingElse("Java Custom")) .build();
为什么这个方案更好?
- 无重复默认值:完全复用数据类本身的默认值,不用在Builder里重复定义,避免后续默认值修改时出现不一致
- 代码简洁:用
?:替代了冗长的if-else判断,逻辑清晰,不容易漏写判断分支 - 双语言友好:同时支持Kotlin的lambda简洁调用和Java的链式调用,满足跨语言需求
- 安全可靠:必传参数用
requireNotNull做非空校验,抛出的异常信息更友好,避免隐性的NullPointerException
内容的提问来源于stack exchange,提问作者Johan
相关产品推荐
相关产品推荐

