API含可空字段时DTO到领域模型的映射方案咨询
解决方案汇总
1. 非空断言(!!)的可行性与风险
- 可行,但仅限你能100%保证服务端返回的bottomSheet非空时,其内部的bottomSheetHeader、bottomSheetText绝对不为空的场景。
- 代码示例:
// DTO类 data class WidgetDto( val widgetHeader: String, val widgetText: String, val bottomSheet: BottomSheetDto? ) data class BottomSheetDto( val bottomSheetHeader: String, val bottomSheetText: String ) // 领域模型 data class Widget( val widgetHeader: String, val widgetText: String, val bottomSheet: BottomSheetData? ) data class BottomSheetData( val bottomSheetHeader: String, val bottomSheetText: String ) // 扩展函数转换 fun WidgetDto.toDomain(): Widget { return Widget( widgetHeader = this.widgetHeader, widgetText = this.widgetText, bottomSheet = this.bottomSheet?.let { BottomSheetData( bottomSheetHeader = it.bottomSheetHeader!!, bottomSheetText = it.bottomSheetText!! ) } ) }
- 风险:如果服务端违反约定返回非空bottomSheet但内部字段为空,
!!会直接抛出NullPointerException导致崩溃。仅适合服务端接口有严格校验、或有完善监控能及时发现数据错误的场景。
2. 空默认值方案
- 可行,适合能接受默认值兜底、优先避免崩溃的场景。
- 代码示例:
fun WidgetDto.toDomain(): Widget { return Widget( widgetHeader = this.widgetHeader, widgetText = this.widgetText, bottomSheet = this.bottomSheet?.let { BottomSheetData( bottomSheetHeader = it.bottomSheetHeader ?: "", bottomSheetText = it.bottomSheetText ?: "" ) } ) }
- 注意:如果业务不允许空字符串,可替换为业务默认值(如"未知标题"),但要确保默认值不会干扰后续业务逻辑。
3. 用requireNotNull做显式校验(推荐)
- 比
!!更安全,断言失败时会抛出带明确信息的异常,便于快速定位问题。 - 代码示例:
fun WidgetDto.toDomain(): Widget { return Widget( widgetHeader = this.widgetHeader, widgetText = this.widgetText, bottomSheet = this.bottomSheet?.let { dto -> BottomSheetData( bottomSheetHeader = requireNotNull(dto.bottomSheetHeader) { "BottomSheet非空时,bottomSheetHeader不能为空" }, bottomSheetText = requireNotNull(dto.bottomSheetText) { "BottomSheet非空时,bottomSheetText不能为空" } ) } ) }
- 优势:既保留了领域模型非空字段的严谨性,又能在服务端违反约定时抛出明确异常,比模糊的NPE更容易排查问题。
4. DTO与领域模型内部字段非空时的编译错误解决
当DTO和领域模型的bottomSheetHeader、bottomSheetText都设为非空,但bottomSheet是可空类型时,编译错误的核心是Kotlin无法识别业务层面的约定(bottomSheet非空则内部字段必非空)。解决办法就是采用上面任意一种方案,本质是在转换时显式处理bottomSheet的可空性,同时对内部字段做非空校验或兜底。
额外优化方向:
- 协调服务端明确接口规则:
bottomSheet要么完全不存在,要么内部字段全部非空,配合Retrofit的严格解析配置(如Moshi开启strict=true),确保解析阶段就拦截不符合约定的数据。 - 使用映射库(如MapStruct、Kotlinx Serialization自定义转换器)集中处理空安全规则,减少手动转换的重复代码。
内容的提问来源于stack exchange,提问作者faritowich
相关产品推荐
相关产品推荐

