Kotlin如何在非数据类间复制属性?含Java第三方类转Kotlin场景
嘿,这个场景我太熟了——手动写一堆属性赋值确实烦死人,尤其是还要穿插处理派生属性的时候。既然Delegates不符合你的使用场景,给你几个更贴合JPA实体类场景的巧妙方案:
方案1:用MapStruct(类型安全的映射神器)
MapStruct是编译时生成映射代码的库,完全类型安全,还支持自定义转换逻辑,特别适合Java→Kotlin实体类的映射,而且和JPA兼容得很好。
步骤:
- 先添加依赖(Kotlin项目要配合
kotlin-kapt插件):
plugins { kotlin("kapt") } dependencies { implementation("org.mapstruct:mapstruct:1.5.5.Final") kapt("org.mapstruct:mapstruct-processor:1.5.5.Final") }
- 定义映射接口,自动处理直接复制的属性,派生属性单独写逻辑:
@Mapper(componentModel = "spring") // 用Spring的话加这个,不用就用"default" interface TestExecutionSummaryMapper { // MapStruct会自动匹配同名属性完成直接复制 fun map(javaSummary: com.yourpackage.TestExecutionSummary): TestExecutionSummary // 针对派生属性,要么用表达式快速处理 @Mapping(target = "derivedStatus", expression = "java(javaSummary.getStatus() + \"_processed\")") // 要么写默认方法处理复杂逻辑 fun mapDerivedStatus(javaSummary: com.yourpackage.TestExecutionSummary): String { return when(javaSummary.status) { "PASSED" -> "SUCCESS" "FAILED" -> "ERROR" else -> "UNKNOWN" } } }
- 使用时直接调用编译自动生成的实现类:
// Spring项目可以直接注入,非Spring就实例化生成的Impl类 val mapper = TestExecutionSummaryMapperImpl() val kotlinEntity = mapper.map(javaTestSummary)
这个方案的核心优势是编译时检查,不会出现运行时的属性名写错、类型不匹配问题,长期维护起来特别省心。
方案2:扩展函数+apply块(轻量无依赖)
如果不想引入第三方库,用Kotlin的扩展函数结合apply块可以大幅减少重复代码,把重复的赋值逻辑封装起来,派生属性单独处理,代码简洁直观。
示例代码:
// 给Java的TestExecutionSummary写个扩展函数,统一处理映射 fun com.yourpackage.TestExecutionSummary.toKotlinEntity(): TestExecutionSummary { return TestExecutionSummary().apply { // 直接复制的属性一次性写完 this.id = this@toKotlinEntity.id this.testName = this@toKotlinEntity.testName this.duration = this@toKotlinEntity.duration this.totalTests = this@toKotlinEntity.totalTests // 派生属性单独写逻辑 this.successRate = if (totalTests > 0) { (this@toKotlinEntity.passedTests.toDouble() / totalTests) * 100 } else 0.0 this.statusLabel = when(this@toKotlinEntity.status) { com.yourpackage.Status.PASSED -> "测试通过" com.yourpackage.Status.FAILED -> "测试失败" else -> "状态未知" } } }
使用时直接调用扩展函数即可:
val kotlinEntity = javaTestSummary.toKotlinEntity()
这个方案零依赖,代码紧凑,适合属性数量不多、映射逻辑不复杂的场景。
方案3:ModelMapper(适合Spring生态)
如果你的项目已经在使用Spring,ModelMapper也是个可选方案——它是运行时映射库,配置灵活,但缺点是没有编译时检查,属性名写错的话要到运行时才会发现。
示例代码:
val modelMapper = ModelMapper() // 配置派生属性的转换规则 modelMapper.typeMap( com.yourpackage.TestExecutionSummary::class.java, TestExecutionSummary::class.java ).addMappings { mapper -> mapper.map( { javaSummary -> if (javaSummary.totalTests > 0) (javaSummary.passedTests * 100.0 / javaSummary.totalTests) else 0.0 }, TestExecutionSummary::successRate ) } val kotlinEntity = modelMapper.map(javaTestSummary, TestExecutionSummary::class.java)
总结一下:
- 属性多、需要长期维护 → 优先选MapStruct,类型安全无后顾之忧
- 不想加依赖、属性少 → 用扩展函数+apply块,轻量简洁
- Spring生态下可以考虑ModelMapper,但要注意运行时风险
内容的提问来源于stack exchange,提问作者Abhijit Sarkar
相关产品推荐
相关产品推荐

