You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 15:54:43