如何在Kotlin中使用泛型扩展函数实现两类data class的映射
错误原因
你写的泛型方法报错是因为Kotlin泛型存在运行时类型擦除,无法直接通过T实例化目标类对象,且不同模型类的字段映射规则存在差异,无法实现无规则的自动通用转换。
推荐实现方案
方案1:无反射类型安全实现(优先推荐)
通过传入映射函数的方式实现,性能更高、逻辑可控,不会有类型安全问题。
// 泛型扩展函数定义 fun <Source, Target> Source.graphqlToDomain(mapper: (Source) -> Target): Target = mapper(this)
使用示例:
// 单对象转换 val graphClient: HomeRequestBodyWithAuthQuery.Client = // 接口返回的GraphQL模型 val domainClient = graphClient.graphqlToDomain { Client(id = it.id, givenName = it.givenName) } // 字段完全匹配时可以直接传入构造函数引用 val domainClient = graphClient.graphqlToDomain(::Client) // 列表转换也可以直接复用 val domainClientList = graphClientList.map { it.graphqlToDomain(::Client) }
方案2:自动映射反射实现
如果你的模型类字段名和类型完全匹配,可以用内联具体化泛型+反射实现无需传参的自动转换,减少重复代码。
- 先添加kotlin反射依赖(build.gradle.kts):
dependencies { implementation("org.jetbrains.kotlin:kotlin-reflect:1.9.0") // 版本和你项目kotlin版本保持一致 }
- 扩展函数实现:
import kotlin.reflect.full.primaryConstructor import kotlin.reflect.full.memberProperties inline fun <reified Target> Any.graphqlToDomain(): Target { val targetClass = Target::class val constructor = targetClass.primaryConstructor ?: throw IllegalArgumentException("${targetClass.simpleName}不存在主构造函数") val sourceFields = this::class.memberProperties.associateBy { it.name } val params = constructor.parameters.associateWith { param -> sourceFields[param.name]?.get(this) } return constructor.callBy(params) }
使用示例:
val domainClient = graphClient.graphqlToDomain<Client>() val domainClientList = graphClientList.map { it.graphqlToDomain<Client>() }
注意:该方案依赖反射,会有少量性能损耗,字段名或类型不匹配时会抛出运行时异常,适合模型字段匹配度极高的场景使用。
内容的提问来源于stack exchange,提问作者Saeed Noshadi
相关产品推荐
相关产品推荐

