Kotlin中如何让配置获取函数直接返回对应类型无需手动转换?
问题描述
现有JPA实体类AppSetting和枚举AppSettingType,用于存储不同类型的应用配置:
@Entity @Table(name = "table_app_settings") data class AppSetting( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "app_setting_id") val id: Long? = null, @Column(name = "app_setting_name") val name: String = "", @Column(name = "app_setting_value") var value: String = "", @Column(name = "app_setting_type") val type: AppSettingType, ) enum class AppSettingType { CHAR, STRING, BYTE, SHORT, INT, LONG, DOUBLE, FLOAT, BOOLEAN, }
当前实现的getAppSetting方法返回DatabaseResult<Any?>,调用时需要手动执行.toString().toInt()这类转换才能拿到目标类型(比如获取barcode_value时)。希望优化方法,让它直接返回指定类型,无需手动转换。
解决方案
核心思路是实现泛型版本的getAppSetting方法,结合AppSettingType做类型校验与自动转换,同时处理类型不匹配的异常场景:
1. 给枚举添加类型映射
先给AppSettingType扩展一个属性,关联对应的Kotlin类型,方便后续校验:
val AppSettingType.targetClass: KClass<*> get() = when(this) { AppSettingType.CHAR -> Char::class AppSettingType.STRING -> String::class AppSettingType.BYTE -> Byte::class AppSettingType.SHORT -> Short::class AppSettingType.INT -> Int::class AppSettingType.LONG -> Long::class AppSettingType.DOUBLE -> Double::class AppSettingType.FLOAT -> Float::class AppSettingType.BOOLEAN -> Boolean::class }
2. 实现泛型版getAppSetting
使用reified关键字获取泛型类型,复用原有转换逻辑并添加类型校验:
inline fun <reified T> getAppSetting(name: String): DatabaseResult<T> { log.info("Getting App Setting $name as type ${T::class.simpleName}") val appSetting = appSettingsRepository.findAppSettingByName(name) return if (appSetting != null) { log.info("App Setting $name found with ID ${appSetting.id}") // 校验请求类型与存储类型是否匹配 if (appSetting.type.targetClass != T::class) { log.error("Mismatched type: $name is stored as ${appSetting.type}, requested ${T::class.simpleName}") return DatabaseResult(code = ResultCode.TYPE_MISMATCH_FAILURE) } // 执行类型转换 val convertedValue = when(appSetting.type) { AppSettingType.CHAR -> appSetting.value.first() as T AppSettingType.STRING -> appSetting.value as T AppSettingType.BYTE -> appSetting.value.toByte() as T AppSettingType.SHORT -> appSetting.value.toShort() as T AppSettingType.INT -> appSetting.value.toInt() as T AppSettingType.LONG -> appSetting.value.toLong() as T AppSettingType.DOUBLE -> appSetting.value.toDouble() as T AppSettingType.FLOAT -> appSetting.value.toFloat() as T AppSettingType.BOOLEAN -> appSetting.value.toBoolean() as T } DatabaseResult(code = ResultCode.FETCH_SUCCESS, entity = convertedValue) } else { log.error("App Setting $name does not exist in database") DatabaseResult(code = ResultCode.FETCH_FAILURE) } }
3. 简化调用方式
现在可以直接指定泛型类型调用,无需手动转换:
// 直接获取Int类型的配置 val barcodeResult = getAppSetting<Int>("barcode_value") // 取出实体,类型为Int? val newBarcode = barcodeResult.entity
4. 可选:优化保存方法
同样可以实现泛型版的保存方法,自动匹配AppSettingType:
inline fun <reified T> saveAppSetting(name: String, value: T): DatabaseResult<T> { val settingType = when(T::class) { Char::class -> AppSettingType.CHAR String::class -> AppSettingType.STRING Byte::class -> AppSettingType.BYTE Short::class -> AppSettingType.SHORT Int::class -> AppSettingType.INT Long::class -> AppSettingType.LONG Double::class -> AppSettingType.DOUBLE Float::class -> AppSettingType.FLOAT Boolean::class -> AppSettingType.BOOLEAN else -> throw IllegalArgumentException("Unsupported type: ${T::class.simpleName}") } val appSetting = AppSetting( name = name, value = value.toString(), type = settingType ) val saveResult = saveAppSetting(appSetting) return if (saveResult.code != ResultCode.CREATION_FAILURE) { DatabaseResult(code = saveResult.code, entity = value) } else { DatabaseResult(code = ResultCode.CREATION_FAILURE) } }
调用保存方法时也无需手动指定类型:
// 自动识别Int类型并保存 saveAppSetting("barcode_value", 177)
补充说明
reified泛型允许在方法内部获取实际的类型信息,无需额外传递类型参数- 类型校验可以提前发现类型不匹配的问题,避免运行时转换异常
- 可以根据业务需求扩展
ResultCode,新增TYPE_MISMATCH_FAILURE来标识类型不匹配的错误场景
内容的提问来源于stack exchange,提问作者JamieRhys
相关产品推荐
相关产品推荐

