Kotlin函数如何自动将Companion类型转为实体类型并支持解构?
问题描述
向submit函数传入T(BLAH1, BLAH1, BLAH2),期望返回T3<BLAH1, BLAH1, BLAH2>类型,但实际得到的是T3<BLAH1.Companion, BLAH1.Companion, BLAH2.Companion>。需要让编译器自动将输入的T<Blah1.Companion...Blahn.Companion>转换为输出的T<Blah1...Blahn>类型,使得解构操作能得到实体类型而非Companion类型。
相关代码实现
转换函数
fun <T1 : R, R> T(value1: T1) = T1<T1, R>(value1) fun <T1 : R, T2 : R, R> T(value1: T1, value2: T2) = T2<T1, T2, R>(value1, value2) fun <T1 : R, T2 : R, T3 : R, R> T(value1: T1, value2: T2, value3: T3) = T3<T1, T2, T3, R>(value1, value2, value3)
T1..Tn 类定义
interface T class T1<out T1 : R, out R>( private val value1: T1, ) : T, Iterable<R> { companion object : T operator fun component1() = value1 override fun iterator(): Iterator<R> = listOf(value1).iterator() } class T2<out T1 : R, out T2 : R, out R>( private val value1: T1, private val value2: T2, ) : T, Iterable<R> { companion object : T operator fun component1() = value1 operator fun component2() = value2 override fun iterator(): Iterator<R> = listOf(value1, value2).iterator() } class T3<out T1 : R, out T2 : R, out T3 : R, out R>( private val value1: T1, private val value2: T2, private val value3: T3, ) : T, Iterable<R> { companion object : T operator fun component1() = value1 operator fun component2() = value2 operator fun component3() = value3 override fun iterator(): Iterator<R> = listOf(value1, value2, value3).iterator() }
输入输出实体类
interface Encodable { val value: String } interface Decodable { val code: Int } class BLAH1(override val value: String) : Encodable { companion object : Decodable { override val code: Int = 1 } } class BLAH2(override val value: String) : Encodable { companion object : Decodable { override val code: Int = 2 } }
submit函数实现
fun <I: Iterable<Encodable>, O: Iterable<Decodable>> submit(i: I, o: O): O { i.forEach { // 处理输入逻辑 println("i: ${it.value}") } // ... // 构造返回结果 val results = mutableListOf<Decodable>() o.forEach { results.add(it) } return when (results.size) { 1 -> T1(BLAH1("1:${results[0]}")) 2 -> T2(BLAH1("1:${results[0]}"), BLAH1("2:${results[1]}")) 3 -> T3(BLAH1("1:${results[0]}"), BLAH1("2:${results[1]}"), BLAH2("3:${results[2]}")) // ... // 支持更多参数的分支 else -> TODO("暂未实现") } as O // 此处类型转换会失败 }
期望使用方式
调用submit后解构结果,变量类型应为实体类而非Companion:
val result = submit( i = T(BLAH1("blah1"), BLAH2("blah2")), o = T(BLAH1, BLAH1, BLAH2) ) val (a, b, c) = result // 期望 a: BLAH1, b: BLAH1, c: BLAH2
实际业务场景
目标是实现数据库查询结果的直接解构,理想API形式如下:
val (a /*: BOOL*/, b /*: INT2*/) = query<BOOL, INT2>("SELECT ...", INT4(42), VARCHAR("123"))
但无法实现泛型参数数量与返回类型O1<A>、O2<A,B>等的对应映射,因此只能将输出类型标记放入函数参数中,导致输入无法使用可变参数,需解决类型转换和API优化问题。
解决方案
1. 重构类型关联:定义Decoder接口
让Companion实现Decoder接口,明确关联对应的实体类型,为后续解码提供类型映射:
interface Decoder<E> { val code: Int fun decode(value: String): E } // 重构实体类 interface Encodable { val value: String } class BLAH1(override val value: String) : Encodable { companion object : Decoder<BLAH1> { override val code: Int = 1 override fun decode(value: String): BLAH1 = BLAH1(value) } } class BLAH2(override val value: String) : Encodable { companion object : Decoder<BLAH2> { override val code: Int = 2 override fun decode(value: String): BLAH2 = BLAH2(value) } }
2. 重新设计Tuple类与构建函数
让T1/T2/T3的泛型参数直接对应返回的实体类型,构建函数接收Decoder实例,明确输入输出的类型关系:
interface ResultTuple class T1<out E>(private val decoder: Decoder<E>) : ResultTuple { operator fun component1(): E = decoder.decode("") // 实际场景替换为查询结果值 } class T2<out E1, out E2>(private val d1: Decoder<E1>, private val d2: Decoder<E2>) : ResultTuple { operator fun component1(): E1 = d1.decode("") operator fun component2(): E2 = d2.decode("") } class T3<out E1, out E2, out E3>(private val d1: Decoder<E1>, private val d2: Decoder<E2>, private val d3: Decoder<E3>) : ResultTuple { operator fun component1(): E1 = d1.decode("") operator fun component2(): E2 = d2.decode("") operator fun component3(): E3 = d3.decode("") } // 构建函数 fun <E> T(decoder: Decoder<E>): T1<E> = T1(decoder) fun <E1, E2> T(d1: Decoder<E1>, d2: Decoder<E2>): T2<E1, E2> = T2(d1, d2) fun <E1, E2, E3> T(d1: Decoder<E1>, d2: Decoder<E2>, d3: Decoder<E3>): T3<E1, E2, E3> = T3(d1, d2, d3)
3. 修改submit函数,实现类型安全返回
现在submit可以根据输入的Tuple类型,返回对应实体类型的结果,无需强制类型转换:
fun <I : Iterable<Encodable>, R : ResultTuple> submit(i: I, o: R): R { i.forEach { println("i: ${it.value}") } // 实际业务中从数据库获取结果后,通过o中的Decoder解码出实体 return o // 替换为真实解码逻辑后的实例 }
4. 优化API:实现泛型驱动的query函数
通过Kotlin具体化泛型和重载函数,实现理想的API形式,自动匹配泛型参数数量与返回Tuple类型:
inline fun <reified E1> query(sql: String, vararg params: Encodable): T1<E1> { val decoder = E1::class.companionObjectInstance as Decoder<E1> return T1(decoder).apply { // 执行查询并解码结果 } } inline fun <reified E1, reified E2> query(sql: String, vararg params: Encodable): T2<E1, E2> { val d1 = E1::class.companionObjectInstance as Decoder<E1> val d2 = E2::class.companionObjectInstance as Decoder<E2> return T2(d1, d2).apply { // 执行查询并解码结果 } } inline fun <reified E1, reified E2, reified E3> query(sql: String, vararg params: Encodable): T3<E1, E2, E3> { val d1 = E1::class.companionObjectInstance as Decoder<E1> val d2 = E2::class.companionObjectInstance as Decoder<E2> val d3 = E3::class.companionObjectInstance as Decoder<E3> return T3(d1, d2, d3).apply { // 执行查询并解码结果 } }
使用示例:
val (a: BLAH1, b: BLAH2) = query<BLAH1, BLAH2>("SELECT ...", INT4(42), VARCHAR("123"))
内容的提问来源于stack exchange,提问作者Jan Vladimir Mostert
相关产品推荐
相关产品推荐

