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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:30:29